diff --git a/src/game/script/C4Script.cpp b/src/game/script/C4Script.cpp index 78ed2c353..dec0cb8b3 100644 --- a/src/game/script/C4Script.cpp +++ b/src/game/script/C4Script.cpp @@ -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 }, diff --git a/src/script/C4Aul.h b/src/script/C4Aul.h index e3bd6c4b4..41587050b 100644 --- a/src/script/C4Aul.h +++ b/src/script/C4Aul.h @@ -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 diff --git a/src/script/C4AulExec.cpp b/src/script/C4AulExec.cpp index 7465c9cad..0cdfeab8a 100644 --- a/src/script/C4AulExec.cpp +++ b/src/script/C4AulExec.cpp @@ -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; diff --git a/src/script/C4AulExec.h b/src/script/C4AulExec.h index a171e584e..a6d7c266e 100644 --- a/src/script/C4AulExec.h +++ b/src/script/C4AulExec.h @@ -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); }; diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 50a05ed2e..f95335f6d 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -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(X)->GetParCount() - 1; - break; + return -reinterpret_cast(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" diff --git a/src/script/C4Value.h b/src/script/C4Value.h index 035ea57c9..0a2350a7f 100644 --- a/src/script/C4Value.h +++ b/src/script/C4Value.h @@ -338,6 +338,20 @@ template <> struct C4ValueConv inline static C4Value *_FromC4V(C4Value &v) { return v._getRef(); } inline static C4Value ToC4V(C4Value *v) { return C4VRef(v); } }; +template <> struct C4ValueConv +{ + 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 +{ + 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 : public C4ValueConv { };