diff --git a/src/CBot/CBotInstr/CBotExpression.cpp b/src/CBot/CBotInstr/CBotExpression.cpp index cb7f3dfc..c98deca5 100644 --- a/src/CBot/CBotInstr/CBotExpression.cpp +++ b/src/CBot/CBotInstr/CBotExpression.cpp @@ -71,6 +71,13 @@ CBotInstr* CBotExpression::Compile(CBotToken* &p, CBotCStack* pStack) return nullptr; } + if ( p->GetType() == ID_SEP ) + { + pStack->SetError(CBotErrNoExpression, p); + delete inst; + return nullptr; + } + inst->m_rightop = CBotExpression::Compile(p, pStack); if (inst->m_rightop == nullptr) { @@ -118,13 +125,13 @@ CBotInstr* CBotExpression::Compile(CBotToken* &p, CBotCStack* pStack) break; case ID_ASSADD: if (type2.Eq(CBotTypBoolean) || - type2.Eq(CBotTypPointer) ) type2 = -1; // numbers and strings + type2.GetType() > CBotTypString ) type2.SetType(-1); // numbers and strings break; case ID_ASSSUB: case ID_ASSMUL: case ID_ASSDIV: case ID_ASSMODULO: - if (type2.GetType() >= CBotTypBoolean) type2 = -1; // numbers only + if (type2.GetType() >= CBotTypBoolean) type2.SetType(-1); // numbers only break; } @@ -179,6 +186,18 @@ bool CBotExpression::Execute(CBotStack* &pj) if ( pile2->GetState()==0) { if (m_rightop && !m_rightop->Execute(pile2)) return false; // initial value // interrupted? + if (m_rightop) + { + CBotVar* var = pile1->GetVar(); + CBotVar* value = pile2->GetVar(); + if (var->GetType() == CBotTypString && value->GetType() != CBotTypString) + { + CBotVar* newVal = CBotVar::Create("", var->GetTypResult()); + value->Update(pj->GetUserPtr()); + newVal->SetValString(value->GetValString()); + pile2->SetVar(newVal); + } + } pile2->IncState(); } diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index 05de1a47..bbe80b27 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -97,9 +97,9 @@ bool TypeCompatible(CBotTypResult& type1, CBotTypResult& type2, int op) if (max == 99) return false; // result is void? // special case for strin concatenation - if (op == ID_ADD && max >= CBotTypString) return true; - if (op == ID_ASSADD && max >= CBotTypString) return true; - if (op == ID_ASS && t1 == CBotTypString) return true; + if (op == ID_ADD && t1 == CBotTypString) return true; + if (op == ID_ASSADD && t2 == CBotTypString) return true; + if (op == ID_ASS && t2 == CBotTypString) return true; if (max >= CBotTypBoolean) { diff --git a/src/CBot/CBotInstr/CBotInstrUtils.h b/src/CBot/CBotInstr/CBotInstrUtils.h index a609988d..b5302883 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.h +++ b/src/CBot/CBotInstr/CBotInstrUtils.h @@ -39,7 +39,13 @@ CBotInstr* CompileParams(CBotToken* &p, CBotCStack* pStack, CBotVar** ppVars); /*! * \brief TypeCompatible Check if two results are consistent to make an - * operation. + * operation. TypeCompatible is used in two ways: + * For non-assignment operations: see CBotTwoOpExpr::Compile + * TypeCompatible( leftType, rightType, opType ) + + * For assignment or compound assignment operations (it's reversed): + * see CBotReturn::Compile & CBotExpression::Compile + * TypeCompatible( valueType, varType, opType ) * \param type1 * \param type2 * \param op diff --git a/src/CBot/CBotInstr/CBotLeftExprVar.cpp b/src/CBot/CBotInstr/CBotLeftExprVar.cpp index 848d16a8..4394fc43 100644 --- a/src/CBot/CBotInstr/CBotLeftExprVar.cpp +++ b/src/CBot/CBotInstr/CBotLeftExprVar.cpp @@ -64,6 +64,12 @@ bool CBotLeftExprVar::Execute(CBotStack* &pj) CBotVar* var2 = pj->GetVar(); // Initial value on the stack if (var2 != nullptr) { + if (m_typevar.Eq(CBotTypString) && var2->GetType() != CBotTypString) + { + var2->Update(pj->GetUserPtr()); + var1->SetValString(var2->GetValString()); + return true; + } var1->SetVal(var2); // Set the value } diff --git a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp index 23e11531..41321032 100644 --- a/src/CBot/CBotInstr/CBotTwoOpExpr.cpp +++ b/src/CBot/CBotInstr/CBotTwoOpExpr.cpp @@ -214,6 +214,13 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera type2 = pStk->GetTypResult(); // what kind of results? + if ( type1.Eq(99) || type2.Eq(99) ) // operand is void + { + pStack->SetError(CBotErrBadType2, &inst->m_token); + delete inst; + return nullptr; + } + // what kind of result? int TypeRes = std::max( type1.GetType(CBotTypResult::GetTypeMode::NULL_AS_POINTER), type2.GetType(CBotTypResult::GetTypeMode::NULL_AS_POINTER) ); if (typeOp == ID_ADD && type1.Eq(CBotTypString)) @@ -267,7 +274,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera return pStack->Return(nullptr, pStk); } - if ( TypeRes != CBotTypString ) + if ( TypeRes != CBotTypString ) // keep string conversion TypeRes = std::max(type1.GetType(), type2.GetType()); inst = i; } @@ -370,7 +377,9 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) // what kind of result? int TypeRes = std::max(type1.GetType(), type2.GetType()); - if ( GetTokenType() == ID_ADD && type1.Eq(CBotTypString) ) + // see "any type convertible chain" in compile method + if ( GetTokenType() == ID_ADD && + (type1.Eq(CBotTypString) || type2.Eq(CBotTypString)) ) { TypeRes = CBotTypString; } @@ -397,7 +406,8 @@ bool CBotTwoOpExpr::Execute(CBotStack* &pStack) CBotVar* result = CBotVar::Create("", TypeRes); // creates a variable to perform the calculation in the appropriate type - TypeRes = std::max(type1.GetType(), type2.GetType()); + if ( TypeRes != CBotTypString ) // keep string conversion + TypeRes = std::max(type1.GetType(), type2.GetType()); if ( GetTokenType() == ID_ADD && type1.Eq(CBotTypString) ) {