Modified parser to use setter bytecode in place of references.

Currently kills:
* a[...] = ... -> Currently a copy is changed and then discarded, need to rework array semantics.
* bla->EffectVar(...) = ... -> Normal EffectVar is rewritten to SetEffectVar - but this won't work for calls. We need a better solution anyway.
* All scripts that use references, obviously. Just have a look at the parser warnings.
stable-5.1
Peter Wortmann 2010-04-07 15:04:19 +02:00
parent 98c34b4fd3
commit efb5925129
6 changed files with 374 additions and 435 deletions

View File

@ -4979,19 +4979,29 @@ static long FnGetEffectCount(C4AulContext *ctx, C4String *psEffectName, C4Object
return pEffect->GetCount(szEffect, iMaxPriority);
}
static C4Value FnEffectVar_C4V(C4AulContext *cthr, C4Value *pviVarIndex, C4Value *pvpObj, C4Value *pviEffectNumber)
static C4Value FnEffectVar(C4AulContext *cthr, int32_t iVarIndex, C4Object *pObj, int32_t iEffectNumber)
{
// get parameters
C4Object *pObj = pvpObj->getObj();
long iVarIndex = pviVarIndex->getInt(), iEffectNumber = pviEffectNumber->getInt();
// safety
if (iVarIndex<0) return C4Value();
// get effect
C4Effect *pEffect = pObj ? pObj->pEffects : Game.pGlobalEffects;
if (!pEffect) return C4Value();
if (!(pEffect = pEffect->Get(iEffectNumber, true))) return C4Value();
// return ref to var
return pEffect->EffectVars[iVarIndex].GetRef();
// return var
return pEffect->EffectVars[iVarIndex];
}
static C4Value FnSetEffectVar(C4AulContext *cthr, int32_t iVarIndex, C4Object *pObj, int32_t iEffectNumber, const C4Value &Value)
{
// safety
if (iVarIndex<0) return C4Value();
// get effect
C4Effect *pEffect = pObj ? pObj->pEffects : Game.pGlobalEffects;
if (!pEffect) return C4Value();
if (!(pEffect = pEffect->Get(iEffectNumber, true))) return C4Value();
// set and return value
pEffect->EffectVars[iVarIndex] = Value;
return Value;
}
static C4Value FnEffectCall_C4V(C4AulContext *ctx, C4Value *pvpTarget, C4Value *pviNumber, C4Value *pvsCallFn, C4Value *pvVal1, C4Value *pvVal2, C4Value *pvVal3, C4Value *pvVal4, C4Value *pvVal5, C4Value *pvVal6, C4Value *pvVal7)
@ -6361,6 +6371,8 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "FatalError", FnFatalError, false);
AddFunc(pEngine, "ExtractMaterialAmount", FnExtractMaterialAmount);
AddFunc(pEngine, "GetEffectCount", FnGetEffectCount);
AddFunc(pEngine, "EffectVar", FnEffectVar);
AddFunc(pEngine, "SetEffectVar", FnSetEffectVar);
AddFunc(pEngine, "PlayVideo", FnPlayVideo);
AddFunc(pEngine, "StartCallTrace", FnStartCallTrace);
AddFunc(pEngine, "StartScriptProfiler", FnStartScriptProfiler);
@ -6829,7 +6841,6 @@ C4ScriptFnDef C4ScriptFnMap[]=
{ "GetEffect", 1 ,C4V_Any ,{ C4V_String ,C4V_C4Object,C4V_Int ,C4V_Int ,C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnGetEffect_C4V, 0 },
{ "CheckEffect", 1 ,C4V_Int ,{ C4V_String ,C4V_C4Object,C4V_Int ,C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnCheckEffect_C4V, 0 },
{ "EffectCall", 1 ,C4V_Any ,{ C4V_C4Object,C4V_Int ,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnEffectCall_C4V, 0 },
{ "EffectVar", 1 ,C4V_Ref ,{ C4V_Int ,C4V_C4Object,C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnEffectVar_C4V, 0 },
{ "AttachMesh", 1 ,C4V_Int ,{ C4V_Any ,C4V_String ,C4V_String ,C4V_Array ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,0 , FnAttachMesh },

View File

@ -130,33 +130,28 @@ struct C4AulParSet
// some special script functions defined hard-coded to reduce the exec context
enum C4AulBCCType
{
AB_ARRAYA_R, // array access
AB_ARRAYA_V, // not creating a reference
AB_ARRAYA, // array or proplist access
AB_ARRAYA_SET,
AB_ARRAY_SLICE, // array slicing
AB_VARN_R, // a named var
AB_VARN_V,
AB_PARN_R, // a named parameter
AB_PARN_V,
AB_LOCALN_R, // a named local
AB_LOCALN_V,
AB_GLOBALN_R, // a named global
AB_GLOBALN_V,
AB_PAR_R, // Par statement
AB_PAR_V,
AB_VARN, // a named var
AB_VARN_SET,
AB_PARN, // a named parameter
AB_PARN_SET,
AB_LOCALN, // a named local
AB_LOCALN_SET,
AB_GLOBALN, // a named global
AB_GLOBALN_SET,
AB_PAR, // Par statement
AB_PAR_SET,
AB_FUNC, // function
// prefix
AB_Inc1, // ++
AB_Dec1, // --
AB_Inc, // ++
AB_Dec, // --
AB_BitNot, // ~
AB_Not, // !
// +
AB_Neg, // -
// postfix (whithout second statement)
AB_Inc1_Postfix, // ++
AB_Dec1_Postfix, // --
// postfix
AB_Pow, // **
AB_Div, // /
@ -175,15 +170,6 @@ enum C4AulBCCType
AB_BitAnd, // &
AB_BitXOr, // ^
AB_BitOr, // |
AB_MulIt, // *=
AB_DivIt, // /=
AB_ModIt, // %=
AB_Inc, // +=
AB_Dec, // -=
AB_AndIt, // &=
AB_OrIt, // |=
AB_XOrIt, // ^=
AB_Set, // =
AB_CALL, // direct object call
AB_CALLFS, // failsafe direct call
@ -194,6 +180,7 @@ enum C4AulBCCType
AB_C4ID, // constant: C4ID
AB_NIL, // constant: nil
AB_ARRAY, // semi-constant: array
AB_DUP, // duplicate value from stack
AB_PROPLIST, // create a new proplist
AB_PROPSET, // set a property of a proplist
AB_IVARN, // initialization of named var
@ -220,7 +207,7 @@ struct C4ScriptOpDef
const char* Identifier;
C4AulBCCType Code;
bool Postfix;
bool RightAssociative; // right oder left-associative?
bool Changer; // changes first operand to result, rewrite to "a = a (op) b"
bool NoSecondStatement; // no second statement expected (++/-- postfix)
C4V_Type RetType; // type returned. ignored by C4V
C4V_Type Type1;
@ -470,6 +457,7 @@ protected:
C4AulFunc *GetFunc(const char *pIdtf); // get local function by name
void AddBCC(C4AulBCCType eType, intptr_t = 0, const char * SPos = 0); // add byte code chunk and advance
void RemoveLastBCC();
void ClearCode();
bool Preparse(); // preparse script; return if successfull
void ParseFn(C4AulScriptFunc *Fn, bool fExprOnly = false); // parse single script function

View File

@ -189,47 +189,50 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
PushValue(C4VNull);
break;
case AB_DUP:
PushValue(pCurVal[-pCPos->Par.i+1]);
break;
case AB_EOFN:
throw new C4AulExecError(pCurCtx->Obj, "function didn't return");
case AB_ERR:
throw new C4AulExecError(pCurCtx->Obj, "syntax error: see previous parser error for details.");
case AB_PARN_R:
PushValueRef(pCurCtx->Pars[pCPos->Par.i]);
break;
case AB_PARN_V:
case AB_PARN:
PushValue(pCurCtx->Pars[pCPos->Par.i]);
break;
case AB_VARN_R:
PushValueRef(pCurCtx->Vars[pCPos->Par.i]);
case AB_PARN_SET:
pCurCtx->Pars[pCPos->Par.i] = pCurVal[0];
break;
case AB_VARN_V:
case AB_VARN:
PushValue(pCurCtx->Vars[pCPos->Par.i]);
break;
case AB_LOCALN_R: case AB_LOCALN_V:
case AB_VARN_SET:
pCurCtx->Vars[pCPos->Par.i] = pCurVal[0];
break;
case AB_LOCALN:
if (!pCurCtx->Obj)
throw new C4AulExecError(pCurCtx->Obj, "can't access local variables in a definition call!");
PushNullVals(1);
pCurCtx->Obj->GetPropertyRef(pCPos->Par.s, pCurVal[0]);
pCurCtx->Obj->GetPropertyVal(pCPos->Par.s, pCurVal[0]);
break;
case AB_LOCALN_SET:
if (!pCurCtx->Obj)
throw new C4AulExecError(pCurCtx->Obj, "can't access local variables in a definition call!");
pCurCtx->Obj->SetProperty(pCPos->Par.s, pCurVal[0]);
break;
case AB_GLOBALN_R:
PushValueRef(*::ScriptEngine.GlobalNamed.GetItem(pCPos->Par.i));
break;
case AB_GLOBALN_V:
case AB_GLOBALN:
PushValue(*::ScriptEngine.GlobalNamed.GetItem(pCPos->Par.i));
break;
// prefix
case AB_Inc1: // ++
CheckOpPar2(pCPos->Par.i);
++(*pCurVal);
break;
case AB_Dec1: // --
CheckOpPar2(pCPos->Par.i);
--(*pCurVal);
case AB_GLOBALN_SET:
::ScriptEngine.GlobalNamed.GetItem(pCPos->Par.i)->Set(pCurVal[0]);
break;
// prefix
case AB_BitNot: // ~
CheckOpPar(pCPos->Par.i);
pCurVal->SetInt(~pCurVal->_getInt());
@ -242,14 +245,13 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
CheckOpPar(pCPos->Par.i);
pCurVal->SetInt(-pCurVal->_getInt());
break;
// postfix (whithout second statement)
case AB_Inc1_Postfix: // ++
CheckOpPar2(pCPos->Par.i);
pCurVal->Set((*pCurVal)++);
case AB_Inc: // ++
CheckOpPar(pCPos->Par.i);
(*pCurVal)++;
break;
case AB_Dec1_Postfix: // --
CheckOpPar2(pCPos->Par.i);
pCurVal->Set((*pCurVal)--);
case AB_Dec: // --
CheckOpPar(pCPos->Par.i);
(*pCurVal)--;
break;
// postfix
case AB_Pow: // **
@ -393,130 +395,6 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
PopValue();
break;
}
case AB_MulIt: // *=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 *= pPar2 ->_getInt();
PopValue();
break;
}
case AB_DivIt: // /=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
if (!pPar2->_getInt())
throw new C4AulExecError(pCurCtx->Obj, "Division by zero");
*pPar1 /= pPar2->_getInt();
PopValue();
break;
}
case AB_ModIt: // %=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
if (!pPar2->_getInt())
throw new C4AulExecError(pCurCtx->Obj, "Division by zero");
*pPar1 %= pPar2->_getInt();
PopValue();
break;
}
case AB_Inc: // +=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 += pPar2 ->_getInt();
PopValue();
break;
}
case AB_Dec: // -=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 -= pPar2 ->_getInt();
PopValue();
break;
}
case AB_AndIt: // &=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 &= pPar2 ->_getInt();
PopValue();
break;
}
case AB_OrIt: // |=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 |= pPar2 ->_getInt();
PopValue();
break;
}
case AB_XOrIt: // ^=
{
CheckOpPars3(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 ^= pPar2 ->_getInt();
PopValue();
break;
}
case AB_Set: // =
{
CheckOpPars(pCPos->Par.i);
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
*pPar1 = *pPar2;
PopValue();
break;
}
/* case AB_UNOP:
{
int iOpID = pCPos->Par.i;
// Typecheck parameter
if(!pCurVal->ConvertTo(C4ScriptOpMap[iOpID].Type1))
throw new C4AulExecError(pCurCtx->Obj,
FormatString("operator \"%s\": got \"%s\", but expected \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pCurVal->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
// Execute operator
if(C4ScriptOpMap[iOpID].Function)
pCurVal->Set((*C4ScriptOpMap[iOpID].Function)(pCurCtx, pCurVal->_getRaw(), 0), C4ScriptOpMap[iOpID].RetType);
else if(C4ScriptOpMap[iOpID].FunctionC4V)
pCurVal->Set((*C4ScriptOpMap[iOpID].FunctionC4V)(pCurCtx, pCurVal, NULL));
break;
}
case AB_BINOP:
{
int iOpID = pCPos->Par.i;
// Get parameters
C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
// Typecheck parameters
if(!pPar1->ConvertTo(C4ScriptOpMap[iOpID].Type1))
throw new C4AulExecError(pCurCtx->Obj,
FormatString("operator \"%s\" left side: got \"%s\", but expected \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pPar1->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
if(!pPar2->ConvertTo(C4ScriptOpMap[iOpID].Type2))
throw new C4AulExecError(pCurCtx->Obj,
FormatString("operator \"%s\" right side: got \"%s\", but expected \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pPar2->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type2)).getData());
// Execute operator
if(C4ScriptOpMap[iOpID].Function)
pPar1->Set((*C4ScriptOpMap[iOpID].Function)(pCurCtx, pPar1->_getRaw(), pPar2->_getRaw()),C4ScriptOpMap[iOpID].RetType);
else if(C4ScriptOpMap[iOpID].FunctionC4V)
pPar1->Set((*C4ScriptOpMap[iOpID].FunctionC4V)(pCurCtx, pPar1, pPar2));
// Pop second parameter
PopValue();
break;
}
*/
case AB_ARRAY:
{
// Create array
@ -555,42 +433,47 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
break;
}
case AB_ARRAYA_R: case AB_ARRAYA_V:
case AB_ARRAYA:
{
C4Value &Index = pCurVal[0];
C4Value &Array = pCurVal[-1].GetRefVal();
// Typcheck
if ((!Array.ConvertTo(C4V_Array) && !Array.ConvertTo(C4V_PropList)) || Array.GetType() == C4V_Any)
throw new C4AulExecError(pCurCtx->Obj, FormatString("array access: can't access %s as an array!", Array.GetTypeName()).getData());
else if (Array.GetType() == C4V_Array)
C4Value *pIndex = pCurVal, *pStruct = pCurVal - 1, *pResult = pCurVal - 1;
// Typcheck to determine whether it's an array or a proplist
if(CheckArrayAccess(pStruct, pIndex) == C4V_Array)
{
if (!Index.ConvertTo(C4V_Int))
throw new C4AulExecError(pCurCtx->Obj, FormatString("array access: index of type %s, int expected!", Index.GetTypeName()).getData());
// Set reference to array element
Array.GetArrayElement(Index._getInt(), pCurVal[-1], pCurCtx, pCPos->bccType == AB_ARRAYA_V);
pStruct->GetArrayElement(pIndex->_getInt(), *pResult, pCurCtx, true);
}
else
{
if (!Index.ConvertTo(C4V_String))
throw new C4AulExecError(pCurCtx->Obj, FormatString("proplist access: index of type %s, string expected!", Index.GetTypeName()).getData());
C4PropList *proplist = Array.getPropList();
assert(proplist);
if (pCPos->bccType == AB_ARRAYA_V)
{
if (!proplist->GetPropertyVal(Index._getStr(), pCurVal[-1]))
{
pCurVal[-1].Set0();
}
}
else
{
proplist->GetPropertyRef(Index._getStr(), pCurVal[-1]);
}
C4PropList *pPropList = pStruct->getPropList();
assert(pPropList);
if (!pPropList->GetPropertyVal(pIndex->_getStr(), *pResult))
pResult->Set0();
}
// Remove index
PopValue();
break;
}
case AB_ARRAYA_SET:
{
C4Value *pValue = pCurVal, *pIndex = pCurVal - 1, *pStruct = pCurVal - 2, *pResult = pCurVal - 2;
// Typcheck to determine whether it's an array or a proplist
if(CheckArrayAccess(pStruct, pIndex) == C4V_Array)
{
// TODO: Does not work, because array gets copied here!
C4Value Ref;
pStruct->GetArrayElement(pIndex->_getInt(), Ref, pCurCtx, false);
Ref.getRef()->Set(*pValue);
}
else
{
C4PropList *pPropList = pStruct->getPropList();
assert(pPropList);
pPropList->SetProperty(pIndex->_getStr(), *pValue);
}
// Set result, remove array and index from stack
*pResult = *pValue;
PopValues(2);
break;
}
case AB_ARRAY_SLICE:
{
C4Value &Array = pCurVal[-2];
@ -731,17 +614,12 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
break;
}
case AB_PAR_R: case AB_PAR_V:
case AB_PAR:
if (!pCurVal->ConvertTo(C4V_Int))
throw new C4AulExecError(pCurCtx->Obj, FormatString("Par: index of type %s, int expected!", pCurVal->GetTypeName()).getData());
// Push reference to parameter on the stack
if (pCurVal->_getInt() >= 0 && pCurVal->_getInt() < pCurCtx->ParCnt())
{
if (pCPos->bccType == AB_PAR_R)
pCurVal->SetRef(&pCurCtx->Pars[pCurVal->_getInt()]);
else
pCurVal->Set(pCurCtx->Pars[pCurVal->_getInt()]);
}
pCurVal->Set(pCurCtx->Pars[pCurVal->_getInt()]);
else
pCurVal->Set0();
break;

View File

@ -141,18 +141,6 @@ private:
FormatString("operator \"%s\" right side: got \"%s\", but expected \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pPar2->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type2)).getData());
}
void CheckOpPars3(int iOpID)
{
CheckOpPars(iOpID);
// Get parameters
C4Value *pPar1 = pCurVal - 1;
// check that the the first parameter references an int, not something else
if (!pPar1->GetRefVal().ConvertTo(C4V_Int))
throw new C4AulExecError(pCurCtx->Obj,
FormatString("operator \"%s\" left side: got reference to \"%s\", but expected reference to \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pPar1->GetRefVal().GetTypeInfo(), GetC4VName(C4V_Int)).getData());
}
void CheckOpPar(int iOpID)
{
// Typecheck parameter
@ -161,16 +149,23 @@ private:
FormatString("operator \"%s\": got \"%s\", but expected \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pCurVal->GetTypeInfo(), GetC4VName(C4ScriptOpMap[iOpID].Type1)).getData());
}
void CheckOpPar2(int iOpID)
{
CheckOpPar(iOpID);
C4Value *pPar1 = pCurVal;
// check that the the first parameter references an int, not something else
if (!pPar1->GetRefVal().ConvertTo(C4V_Int))
throw new C4AulExecError(pCurCtx->Obj,
FormatString("operator \"%s\": got reference to \"%s\", but expected reference to \"%s\"!",
C4ScriptOpMap[iOpID].Identifier, pPar1->GetRefVal().GetTypeInfo(), GetC4VName(C4V_Int)).getData());
C4V_Type CheckArrayAccess(C4Value *pStructure, C4Value *pIndex)
{
if (pStructure->ConvertTo(C4V_Array) && pStructure->GetType() != C4V_Any)
{
if (!pIndex->ConvertTo(C4V_Int))
throw new C4AulExecError(pCurCtx->Obj, FormatString("array access: index of type %s, int expected!", pIndex->GetTypeName()).getData());
return C4V_Array;
}
else if (pStructure->ConvertTo(C4V_PropList) && pStructure->GetType() != C4V_Any)
{
if (!pIndex->ConvertTo(C4V_String))
throw new C4AulExecError(pCurCtx->Obj, FormatString("proplist access: index of type %s, string expected!", pIndex->GetTypeName()).getData());
return C4V_PropList;
}
else
throw new C4AulExecError(pCurCtx->Obj, FormatString("can't access %s as array or proplist!", pStructure->GetTypeName()).getData());
}
C4AulBCC *Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4Object *pObj = NULL, C4Def *pDef = NULL);
};

View File

@ -46,7 +46,7 @@
#define C4AUL_If "if"
#define C4AUL_Else "else"
#define C4AUL_Do "do"
#define C4AUL_Do "do"
#define C4AUL_While "while"
#define C4AUL_For "for"
#define C4AUL_In "in"
@ -116,6 +116,7 @@ enum C4AulTokenType
ATT_AMP, // "&"
ATT_TILDE, // '~'
ATT_LDOTS, // '...'
ATT_SET, // '='
ATT_OPERATOR,// operator
ATT_EOF // end of file
};
@ -183,9 +184,11 @@ private:
bool fJump;
int iStack;
int GetStackValue(C4AulBCCType eType, intptr_t X = 0);
void AddBCC(C4AulBCCType eType, intptr_t X = 0);
void RemoveLastBCC();
void SetNoRef(); // Switches the bytecode to generate a value instead of a reference
C4AulBCC MakeSetter(bool fLeaveValue = false); // Prepares to generate a setter for the last value that was generated
int JumpHere(); // Get position for a later jump to next instruction added
void SetJumpHere(int iJumpOp); // Use the next inserted instruction as jump target for the given jump operation
@ -417,22 +420,22 @@ bool C4AulParseState::AdvanceSpaces()
C4ScriptOpDef C4ScriptOpMap[] =
{
// priority postfix
// | identifier | right-associative
// | identifier | changer
// | | Bytecode | | no second id
// | | | | | | RetType ParType1 ParType2
// prefix
{ 15, "++", AB_Inc1, 0, 1, 0, C4V_Int, C4V_Ref, C4V_Any},
{ 15, "--", AB_Dec1, 0, 1, 0, C4V_Int, C4V_Ref, C4V_Any},
{ 15, "~", AB_BitNot, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "!", AB_Not, 0, 1, 0, C4V_Bool, C4V_Bool, C4V_Any},
{ 15, "+", AB_ERR, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "-", AB_Neg, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "++", AB_Inc, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "--", AB_Dec, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "~", AB_BitNot, 0, 0, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "!", AB_Not, 0, 0, 0, C4V_Bool, C4V_Bool, C4V_Any},
{ 15, "+", AB_ERR, 0, 0, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "-", AB_Neg, 0, 0, 0, C4V_Int, C4V_Int, C4V_Any},
// postfix (whithout second statement)
{ 16, "++", AB_Inc1_Postfix, 1, 1, 1, C4V_Int, C4V_Ref, C4V_Any},
{ 16, "--", AB_Dec1_Postfix, 1, 1, 1, C4V_Int, C4V_Ref, C4V_Any},
// postfix
{ 16, "++", AB_Inc, 1, 1, 1, C4V_Int, C4V_Int, C4V_Any},
{ 16, "--", AB_Dec, 1, 1, 1, C4V_Int, C4V_Int, C4V_Any},
// postfix
{ 14, "**", AB_Pow, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 13, "/", AB_Div, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 13, "*", AB_Mul, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
@ -452,16 +455,17 @@ C4ScriptOpDef C4ScriptOpMap[] =
{ 6, "|", AB_BitOr, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 5, "&&", AB_JUMPAND, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 4, "||", AB_JUMPOR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 2, "*=", AB_MulIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "/=", AB_DivIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "%=", AB_ModIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "+=", AB_Inc, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "-=", AB_Dec, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "&=", AB_AndIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "|=", AB_OrIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "^=", AB_XOrIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "=", AB_Set, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Any},
// changers
{ 2, "*=", AB_Mul, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "/=", AB_Div, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "%=", AB_Mod, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "+=", AB_Sum, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "-=", AB_Sub, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "&=", AB_BitAnd, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "|=", AB_BitOr, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 2, "^=", AB_BitXOr, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
{ 0, NULL, AB_ERR, 0, 0, 0, C4V_Any, C4V_Any, C4V_Any}
};
@ -636,6 +640,12 @@ C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldS
SPos += SLen(C4ScriptOpMap[iOpID].Identifier);
return ATT_OPERATOR;
}
// set?
if (*SPos == '=')
{
SPos++;
return ATT_SET;
}
}
else if (C == '*') { SPos++; return ATT_STAR; } // "*"
else if (C == '&') { SPos++; return ATT_AMP; } // "&"
@ -831,33 +841,28 @@ static const char * GetTTName(C4AulBCCType e)
{
switch (e)
{
case AB_ARRAYA_R: return "ARRAYA_R"; // array access
case AB_ARRAYA_V: return "ARRAYA_V"; // not creating a reference
case AB_ARRAYA: return "ARRAYA"; // array access
case AB_ARRAYA_SET: return "ARRAYA_SET"; // setter
case AB_ARRAY_SLICE: return "ARRAY_SLICE";
case AB_VARN_R: return "VARN_R"; // a named var
case AB_VARN_V: return "VARN_V";
case AB_PARN_R: return "PARN_R"; // a named parameter
case AB_PARN_V: return "PARN_V";
case AB_LOCALN_R: return "LOCALN_R"; // a named local
case AB_LOCALN_V: return "LOCALN_V";
case AB_GLOBALN_R: return "GLOBALN_R"; // a named global
case AB_GLOBALN_V: return "GLOBALN_V";
case AB_PAR_R: return "PAR_R"; // Par statement
case AB_PAR_V: return "PAR_V";
case AB_VARN: return "VARN"; // a named var
case AB_VARN_SET: return "VARN_SET";
case AB_PARN: return "PARN"; // a named parameter
case AB_PARN_SET: return "PARN_SET";
case AB_LOCALN: return "LOCALN"; // a named local
case AB_LOCALN_SET: return "LOCALN_SET";
case AB_GLOBALN: return "GLOBALN"; // a named global
case AB_GLOBALN_SET: return "GLOBALN_SET";
case AB_PAR: return "PAR"; // Par statement
case AB_PAR_SET: return "PAR_SET";
case AB_FUNC: return "FUNC"; // function
// prefix
case AB_Inc1: return "Inc1"; // ++
case AB_Dec1: return "Dec1"; // --
case AB_Inc: return "Inc"; // ++
case AB_Dec: return "Dec"; // --
case AB_BitNot: return "BitNot"; // ~
case AB_Not: return "Not"; // !
// +
case AB_Neg: return "Neg"; // -
// postfix (whithout second statement)
case AB_Inc1_Postfix: return "Inc1_Postfix"; // ++
case AB_Dec1_Postfix: return "Dec1_Postfix"; // --
// postfix
case AB_Pow: return "Pow"; // **
case AB_Div: return "Div"; // /
@ -876,15 +881,6 @@ static const char * GetTTName(C4AulBCCType e)
case AB_BitAnd: return "BitAnd"; // &
case AB_BitXOr: return "BitXOr"; // ^
case AB_BitOr: return "BitOr"; // |
case AB_MulIt: return "MulIt"; // *=
case AB_DivIt: return "DivIt"; // /=
case AB_ModIt: return "ModIt"; // %=
case AB_Inc: return "Inc"; // +=
case AB_Dec: return "Dec"; // -=
case AB_AndIt: return "AndIt"; // &=
case AB_OrIt: return "OrIt"; // |=
case AB_XOrIt: return "XOrIt"; // ^=
case AB_Set: return "Set"; // =
case AB_CALL: return "CALL"; // direct object call
case AB_CALLFS: return "CALLFS"; // failsafe direct call
@ -894,6 +890,7 @@ static const char * GetTTName(C4AulBCCType e)
case AB_STRING: return "STRING"; // constant: string
case AB_C4ID: return "C4ID"; // constant: C4ID
case AB_NIL: return "NIL"; // constant: nil
case AB_DUP: return "DUP"; // constant: nil
case AB_ARRAY: return "ARRAY"; // semi-constant: array
case AB_PROPLIST: return "PROPLIST"; // semi-constant: array
case AB_PROPSET: return "PROPSET";
@ -935,7 +932,8 @@ void C4AulScript::AddBCC(C4AulBCCType eType, intptr_t X, const char * SPos)
CPos->SPos = SPos;
switch (eType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN_R: case AB_LOCALN_V:
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN:
/* case AB_LOCALN_SET: -- expected to already have a reference upon creation, see MakeSetter */
CPos->Par.s->IncRef();
break;
default:
@ -945,20 +943,24 @@ void C4AulScript::AddBCC(C4AulBCCType eType, intptr_t X, const char * SPos)
CPos++; CodeSize++;
}
void C4AulScript::RemoveLastBCC()
{
C4AulBCC *pBCC = Code + CodeSize - 1;
switch (pBCC->bccType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET:
pBCC->Par.s->DecRef();
break;
default: break; // I don't want to do anything, thank you very much. Stupid warnings.
}
CodeSize--;
CPos--;
}
void C4AulScript::ClearCode()
{
for (int i = 0; i < CodeSize; ++i)
{
switch (Code[i].bccType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN_R: case AB_LOCALN_V:
Code[i].Par.s->DecRef();
break;
default:
// TODO
break;
}
}
while(CodeSize > 0)
RemoveLastBCC();
delete[] Code;
Code = 0;
CodeSize = CodeBufSize = 0;
@ -1008,10 +1010,9 @@ bool C4AulScript::Preparse()
return true;
}
void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
int C4AulParseState::GetStackValue(C4AulBCCType eType, intptr_t X)
{
if (Type != PARSER) return;
// Track stack size
switch (eType)
{
case AB_INT:
@ -1020,16 +1021,12 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
case AB_C4ID:
case AB_PROPLIST:
case AB_NIL:
case AB_VARN_R:
case AB_VARN_V:
case AB_PARN_R:
case AB_PARN_V:
case AB_LOCALN_R:
case AB_LOCALN_V:
case AB_GLOBALN_R:
case AB_GLOBALN_V:
iStack++;
break;
case AB_VARN:
case AB_PARN:
case AB_LOCALN:
case AB_GLOBALN:
case AB_DUP:
return 1;
case AB_Pow:
case AB_Div:
@ -1048,17 +1045,7 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
case AB_BitAnd:
case AB_BitXOr:
case AB_BitOr:
case AB_MulIt:
case AB_DivIt:
case AB_ModIt:
case AB_Inc:
case AB_Dec:
case AB_AndIt:
case AB_OrIt:
case AB_XOrIt:
case AB_Set:
case AB_ARRAYA_R:
case AB_ARRAYA_V:
case AB_ARRAYA:
case AB_CONDN:
case AB_COND:
case AB_IVARN:
@ -1067,51 +1054,57 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
// or decrement the stack. Thus, for stack counting purposes, they decrement.
case AB_JUMPAND:
case AB_JUMPOR:
iStack--;
break;
return -1;
case AB_FUNC:
iStack-= reinterpret_cast<C4AulFunc *>(X)->GetParCount() - 1;
break;
return -reinterpret_cast<C4AulFunc *>(X)->GetParCount() + 1;
case AB_CALL:
case AB_CALLFS:
iStack-=C4AUL_MAX_Par;
break;
return -C4AUL_MAX_Par;
case AB_Inc1:
case AB_Dec1:
case AB_VARN_SET:
case AB_PARN_SET:
case AB_LOCALN_SET:
case AB_GLOBALN_SET:
case AB_PAR_SET:
case AB_Inc:
case AB_Dec:
case AB_BitNot:
case AB_Not:
case AB_Neg:
case AB_Inc1_Postfix:
case AB_Dec1_Postfix:
case AB_PAR_R:
case AB_PAR_V:
case AB_PAR:
case AB_FOREACH_NEXT:
case AB_ERR:
case AB_EOFN:
case AB_EOF:
case AB_JUMP:
case AB_DEBUG:
break;
return 0;
case AB_STACK:
iStack+=X;
break;
return X;
case AB_ARRAY:
iStack-=X-1;
break;
return -X+1;
case AB_ARRAYA_SET:
case AB_ARRAY_SLICE:
case AB_PROPSET:
iStack -= 2;
break;
return -2;
default:
assert(false);
}
return 0;
}
void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
{
if (Type != PARSER) return;
// Track stack size
iStack += GetStackValue(eType, X);
// Use stack operation instead of 0-Any (enable optimization)
if (eType == AB_NIL)
@ -1121,24 +1114,40 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
}
// Join checks only if it's not a jump target
if (!fJump)
if (!fJump && a->CPos > a->Code)
{
// Join together stack operations
if (eType == AB_STACK &&
a->CPos > a->Code &&
(a->CPos-1)->bccType == AB_STACK
&& (X <= 0 || (a->CPos-1)->Par.i >= 0))
C4AulBCC *pCPos1 = a->CPos - 1;
if(eType == AB_STACK && pCPos1->bccType == AB_STACK &&
(X <= 0 || pCPos1->Par.i >= 0))
{
(a->CPos-1)->Par.i += X;
pCPos1->Par.i += X;
// Empty? Remove it.
if (!(a->CPos-1)->Par.i)
{
a->CPos--;
a->CodeSize--;
}
if (!pCPos1->Par.i)
a->RemoveLastBCC();
return;
}
// Join VARN_SET + STACK -1 to IVARN (equivalent)
if(eType == AB_STACK && X == -1 && pCPos1->bccType == AB_VARN_SET)
{
pCPos1->bccType = AB_IVARN;
return;
}
// Reduce some constructs like SUM + INT 1 to INC or DEC
if((eType == AB_Sum || eType == AB_Sub) &&
pCPos1->bccType == AB_INT &&
(pCPos1->Par.i == 1 || pCPos1->Par.i == -1))
{
if((pCPos1->Par.i > 0) == (eType == AB_Sum))
pCPos1->bccType = AB_Inc;
else
pCPos1->bccType = AB_Dec;
pCPos1->Par.i = X;
return;
}
}
// Add
@ -1148,20 +1157,64 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
fJump = false;
}
void C4AulParseState::SetNoRef()
void C4AulParseState::RemoveLastBCC()
{
if (Type != PARSER) return;
C4AulBCC * CPos = a->CPos - 1;
switch (CPos->bccType)
// Security: This is unsafe on anything that might get optimized away
C4AulBCC *pBCC = a->CPos-1;
assert(pBCC->bccType != AB_STACK);
// Correct stack
iStack -= GetStackValue(pBCC->bccType, pBCC->Par.i);
// Remove
a->RemoveLastBCC();
}
C4AulBCC C4AulParseState::MakeSetter(bool fLeaveValue)
{
if(Type != PARSER) { C4AulBCC Dummy; Dummy.bccType = AB_ERR; return Dummy; }
C4AulBCC Value = *(a->CPos - 1), Setter = Value;
// Check type
switch (Value.bccType)
{
case AB_ARRAYA_R: CPos->bccType = AB_ARRAYA_V; break;
case AB_PAR_R: CPos->bccType = AB_PAR_V; break;
case AB_PARN_R: CPos->bccType = AB_PARN_V; break;
case AB_VARN_R: CPos->bccType = AB_VARN_V; break;
case AB_LOCALN_R: CPos->bccType = AB_LOCALN_V; break;
case AB_GLOBALN_R: CPos->bccType = AB_GLOBALN_V; break;
default: break;
case AB_ARRAYA: Setter.bccType = AB_ARRAYA_SET; break;
case AB_PAR: Setter.bccType = AB_PAR_SET; break;
case AB_PARN: Setter.bccType = AB_PARN_SET; break;
case AB_VARN: Setter.bccType = AB_VARN_SET; break;
case AB_LOCALN:
Setter.bccType = AB_LOCALN_SET;
Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC
break;
case AB_GLOBALN: Setter.bccType = AB_GLOBALN_SET; break;
case AB_CALL:
// Huge hacks would required to make this work. EffectVar should get the Var treatment
// and become a BCC of its own anyway.
throw new C4AulParseError(this, "EffectVar call not supported right now, sorry!");
case AB_FUNC:
// This one at least works somewhat
if(SEqual(Value.Par.f->Name, "EffectVar"))
{
Setter.Par.f = a->GetFuncRecursive("SetEffectVar");
break;
}
// falthru
default:
throw new C4AulParseError(this, "assignment not possible for this value!");
}
// Remove value BCC
RemoveLastBCC();
// Want the value?
if(fLeaveValue)
{
// Duplicate parameters on stack
// (all push one value on the stack as result, so we have -(N-1) parameters)
int iParCount = -GetStackValue(Value.bccType, Value.Par.i) + 1;
for(int i = 0; i < iParCount; i++)
AddBCC(AB_DUP, iParCount);
// Finally re-add original BCC
AddBCC(Value.bccType, Value.Par.i);
}
// Done. The returned BCC should be added later once the value to be set was pushed on top.
assert(GetStackValue(Value.bccType, Value.Par.i) == GetStackValue(Setter.bccType, Setter.Par.i)+1);
return Setter;
}
int C4AulParseState::JumpHere()
@ -1703,6 +1756,7 @@ void C4AulParseState::Parse_Statement()
}
case ATT_BOPEN:
case ATT_BOPEN2:
case ATT_SET:
case ATT_OPERATOR:
case ATT_INT: // constant in cInt
case ATT_BOOL: // constant in cInt
@ -1710,7 +1764,6 @@ void C4AulParseState::Parse_Statement()
case ATT_C4ID: // converted ID in cInt
{
Parse_Expression();
SetNoRef();
AddBCC(AB_STACK, -1);
Match(ATT_SCOLON);
return;
@ -1815,8 +1868,6 @@ void C4AulParseState::Parse_Statement()
// return retval;
Parse_Expression();
}
if (!Fn->bReturnRef)
SetNoRef();
AddBCC(AB_RETURN);
}
else if (SEqual(Idtf, C4AUL_Break)) // break
@ -1862,7 +1913,6 @@ void C4AulParseState::Parse_Statement()
else
{
Parse_Expression();
SetNoRef();
AddBCC(AB_STACK, -1);
}
Match(ATT_SCOLON);
@ -1912,7 +1962,7 @@ int C4AulParseState::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * p
int i = Fn->ParNamed.iSize;
while (size < iMaxCnt && i < C4AUL_MAX_Par)
{
AddBCC(AB_PARN_R, i);
AddBCC(AB_PARN, i);
++i;
++size;
}
@ -1927,15 +1977,6 @@ int C4AulParseState::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * p
Parse_Expression();
if (pFunc && (Type == PARSER))
{
bool anyfunctakesref = (pFunc->GetParType()[size] == C4V_Ref);
// pFunc either was the return value from a GetFirstFunc-Call or
// pFunc is the only function that could be called, so this loop is superflous
C4AulFunc * pFunc2 = pFunc;
while ((pFunc2 = a->Engine->GetNextSNFunc(pFunc2)))
if (pFunc2->GetParType()[size] == C4V_Ref) anyfunctakesref = true;
// Change the bytecode to the equivalent that does not produce a reference.
if (!anyfunctakesref)
SetNoRef();
C4V_Type from = C4V_Any;
C4V_Type to = pFunc->GetParType()[size];
switch ((a->CPos-1)->bccType)
@ -1944,7 +1985,6 @@ int C4AulParseState::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * p
case AB_STRING: from = C4V_String; break;
case AB_ARRAY: case AB_ARRAY_SLICE: from = C4V_Array; break;
case AB_BOOL: from = C4V_Bool; break;
// case AB_UNOP: case AB_BINOP: from = C4ScriptOpMap[(a->CPos-1)->Par.i].RetType; break;
case AB_FUNC:
if ((a->CPos-1)->Par.f) from = (a->CPos-1)->Par.f->GetRetType(); break;
case AB_CALL: case AB_CALLFS:
@ -1959,8 +1999,6 @@ int C4AulParseState::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * p
pFunc2 = a->Engine->GetNextSNFunc(pFunc2);
}
}
case AB_ARRAYA_R: case AB_PAR_R: case AB_PARN_R: case AB_VARN_R: case AB_LOCALN_R: case AB_GLOBALN_R:
from = C4V_Ref; break;
default: break; // avoid compiler warning about unhandled enumerators
}
if (C4Value::WarnAboutConversion(from, to))
@ -2067,7 +2105,7 @@ void C4AulParseState::Parse_PropList()
Shift();
}
else UnexpectedToken("string or identifier");
if (TokenType != ATT_COLON && (TokenType != ATT_OPERATOR || !SEqual(C4ScriptOpMap[cInt].Identifier,"=")))
if (TokenType != ATT_COLON && TokenType != ATT_SET)
UnexpectedToken("':' or '='");
Shift();
Parse_Expression();
@ -2094,7 +2132,6 @@ void C4AulParseState::Parse_DoWhile()
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
SetNoRef();
// Jump back
AddJump(AB_COND, iStart);
if (Type != PARSER) return;
@ -2115,7 +2152,6 @@ void C4AulParseState::Parse_While()
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
SetNoRef();
// Check condition
int iCond = a->GetCodePos();
AddBCC(AB_CONDN);
@ -2142,7 +2178,6 @@ void C4AulParseState::Parse_If()
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
SetNoRef();
// create bytecode, remember position
int iCond = a->GetCodePos();
AddBCC(AB_CONDN);
@ -2177,7 +2212,6 @@ void C4AulParseState::Parse_For()
else if (TokenType != ATT_SCOLON)
{
Parse_Expression();
SetNoRef();
AddBCC(AB_STACK, -1);
}
// Consume first semicolon
@ -2189,7 +2223,6 @@ void C4AulParseState::Parse_For()
// Add condition code
iCondition = JumpHere();
Parse_Expression();
SetNoRef();
// Jump out
iJumpOut = a->GetCodePos();
AddBCC(AB_CONDN);
@ -2206,7 +2239,6 @@ void C4AulParseState::Parse_For()
// Add incrementor code
iIncrementor = JumpHere();
Parse_Expression();
SetNoRef();
AddBCC(AB_STACK, -1);
// Jump to condition
if (iCondition != -1)
@ -2307,14 +2339,14 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
if (Fn->ParNamed.GetItemNr(Idtf) != -1)
{
// insert variable by id
AddBCC(AB_PARN_R, Fn->ParNamed.GetItemNr(Idtf));
AddBCC(AB_PARN, Fn->ParNamed.GetItemNr(Idtf));
Shift();
}
// check for variable (var)
else if (Fn->VarNamed.GetItemNr(Idtf) != -1)
{
// insert variable by id
AddBCC(AB_VARN_R, Fn->VarNamed.GetItemNr(Idtf));
AddBCC(AB_VARN, Fn->VarNamed.GetItemNr(Idtf));
Shift();
}
// check for variable (local)
@ -2325,14 +2357,14 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
throw new C4AulParseError(this, "using local variable in global function!");
// insert variable by id
C4String * pKey = Strings.RegString(Idtf);
AddBCC(AB_LOCALN_R, (intptr_t) pKey);
AddBCC(AB_LOCALN, (intptr_t) pKey);
Shift();
}
// check for global variable (static)
else if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
{
// insert variable by id
AddBCC(AB_GLOBALN_R, a->Engine->GlobalNamedNames.GetItemNr(Idtf));
AddBCC(AB_GLOBALN, a->Engine->GlobalNamedNames.GetItemNr(Idtf));
Shift();
}
// function identifier: check special functions
@ -2357,7 +2389,7 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
// and for Par
Shift();
Parse_Params(1, C4AUL_Par);
AddBCC(AB_PAR_R);
AddBCC(AB_PAR);
}
else if (SEqual(Idtf, C4AUL_Inherited) || SEqual(Idtf, C4AUL_SafeInherited))
{
@ -2501,8 +2533,15 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
(a->CPos - 1)->Par.i = -(a->CPos - 1)->Par.i;
break;
}
// changer? make a setter BCC, leave value for operator
C4AulBCC Changer;
if(C4ScriptOpMap[OpID].Changer)
Changer = MakeSetter(true);
// write byte code
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
// writter setter
if(C4ScriptOpMap[OpID].Changer)
AddBCC(Changer.bccType, Changer.Par.i);
break;
}
case ATT_BOPEN:
@ -2536,6 +2575,21 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
{
while (1) switch (TokenType)
{
case ATT_SET:
{
// back out of any kind of parent operator
// (except other setters, as those are right-associative)
if(iParentPrio > 1)
return;
// generate setter
C4AulBCC Setter = MakeSetter(false);
// parse value to set
Shift();
Parse_Expression(1);
// write setter
AddBCC(Setter.bccType, Setter.Par.i);
break;
}
case ATT_OPERATOR:
{
// expect postfix operator
@ -2557,14 +2611,25 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
// otherwise use the new-found correct postfix operator
OpID = nOpID;
}
// lower priority?
if (C4ScriptOpMap[OpID].RightAssociative ?
C4ScriptOpMap[OpID].Priority < iParentPrio :
C4ScriptOpMap[OpID].Priority <= iParentPrio)
return;
// If the operator does not modify the first argument, no reference is necessary
if (C4ScriptOpMap[OpID].Type1 != C4V_Ref)
SetNoRef();
// changer?
C4AulBCC Setter;
if (C4ScriptOpMap[OpID].Changer)
{
// changer: back out only if parent operator is stronger
// (everything but setters and other changers, as changers are right-associative)
if(iParentPrio > C4ScriptOpMap[OpID].Priority)
return;
// generate setter, leave value on stack for operator
Setter = MakeSetter(true);
}
else
{
// normal operator: back out if parent operator is at least as strong
// (non-setter operators are left-associative)
if(iParentPrio >= C4ScriptOpMap[OpID].Priority)
return;
}
Shift();
if (C4ScriptOpMap[OpID].Code == AB_JUMPAND || C4ScriptOpMap[OpID].Code == AB_JUMPOR)
@ -2577,20 +2642,21 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
Parse_Expression(C4ScriptOpMap[OpID].Priority);
// set condition jump target
SetJumpHere(iCond);
// write setter (unused - could also optimize to skip self-assign, but must keep stack balanced)
if (C4ScriptOpMap[OpID].Changer)
AddBCC(Setter.bccType, Setter.Par.i);
break;
}
else
{
// expect second parameter for operator
if (!C4ScriptOpMap[OpID].NoSecondStatement)
{
Parse_Expression(C4ScriptOpMap[OpID].Priority);
// If the operator does not modify the second argument, no reference is necessary
if (C4ScriptOpMap[OpID].Type2 != C4V_Ref)
SetNoRef();
}
// write byte code
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
// write setter
if (C4ScriptOpMap[OpID].Changer)
AddBCC(Setter.bccType, Setter.Par.i);
}
break;
}
@ -2606,7 +2672,7 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
if (TokenType == ATT_BCLOSE2)
{
Shift();
AddBCC(AB_ARRAYA_R);
AddBCC(AB_ARRAYA);
}
else if (TokenType == ATT_COLON)
{
@ -2692,36 +2758,23 @@ void C4AulParseState::Parse_Var()
if (iVarID < 0)
throw new C4AulParseError(this, "internal error: var definition: var not found in variable table");
Shift();
if (TokenType == ATT_OPERATOR)
if(TokenType == ATT_SET)
{
// only accept "="
int iOpID = cInt;
if (SEqual(C4ScriptOpMap[iOpID].Identifier,"="))
{
// insert initialization in byte code
Shift();
Parse_Expression();
AddBCC(AB_IVARN, iVarID);
}
else
throw new C4AulParseError(this, "unexpected operator");
// insert initialization in byte code
Shift();
Parse_Expression();
AddBCC(AB_IVARN, iVarID);
}
switch (TokenType)
{
case ATT_COMMA:
{
Shift();
break;
}
case ATT_SCOLON:
{
return;
}
default:
{
UnexpectedToken("',' or ';'");
}
}
}
}
@ -2818,7 +2871,7 @@ void C4AulParseState::Parse_Const()
Error("constant and variable with name ", Idtf);
Match(ATT_IDTF);
// expect '='
if (TokenType != ATT_OPERATOR || !SEqual(C4ScriptOpMap[cInt].Identifier,"="))
if (TokenType != ATT_SET)
UnexpectedToken("'='");
// expect value. Theoretically, something like C4AulScript::ExecOperator could be used here
// this would allow for definitions like "static const OCF_Edible = 1<<23"

View File

@ -338,6 +338,20 @@ template <> struct C4ValueConv<C4Value *>
inline static C4Value *_FromC4V(C4Value &v) { return v._getRef(); }
inline static C4Value ToC4V(C4Value *v) { return C4VRef(v); }
};
template <> struct C4ValueConv<const C4Value &>
{
inline static C4V_Type Type() { return C4V_Any; }
inline static const C4Value &FromC4V(C4Value &v) { return v; }
inline static const C4Value &_FromC4V(C4Value &v) { return v; }
inline static C4Value ToC4V(const C4Value &v) { return v; }
};
template <> struct C4ValueConv<C4Value>
{
inline static C4V_Type Type() { return C4V_Any; }
inline static C4Value FromC4V(C4Value &v) { return v; }
inline static C4Value _FromC4V(C4Value &v) { return v; }
inline static C4Value ToC4V(C4Value v) { return v; }
};
// aliases
template <> struct C4ValueConv<long> : public C4ValueConv<int32_t> { };