Merge pull request #897 from melex750/dev
* Fix game crashing related to syntax errors * Fix problems with not returning correct value from function * Add default values for parameters * Fix custom functions not accepting nan * Fix 'point' constructor not executing when called with newdev-buzzingcars
commit
967fb9e30f
|
@ -1739,6 +1739,15 @@ msgstr ""
|
|||
msgid "Function needs return type \"void\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Class name expected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Non-void function needs \"return;\""
|
||||
msgstr ""
|
||||
|
||||
msgid "This parameter needs a default value"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dividing by zero"
|
||||
msgstr ""
|
||||
|
||||
|
|
9
po/de.po
9
po/de.po
|
@ -356,6 +356,9 @@ msgstr ""
|
|||
msgid "Checkpoint"
|
||||
msgstr "Checkpoint"
|
||||
|
||||
msgid "Class name expected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Climb\\Increases the power of the jet"
|
||||
msgstr "Steigen\\Leistung des Triebwerks steigern"
|
||||
|
||||
|
@ -939,6 +942,9 @@ msgstr "Kein konvertierbares Platin"
|
|||
msgid "No userlevels installed!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Non-void function needs \"return;\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Normal size"
|
||||
msgstr "Normale Größe"
|
||||
|
||||
|
@ -1516,6 +1522,9 @@ msgstr ""
|
|||
msgid "This object is not a member of a class"
|
||||
msgstr "Das Objekt ist nicht eine Instanz einer Klasse"
|
||||
|
||||
msgid "This parameter needs a default value"
|
||||
msgstr ""
|
||||
|
||||
msgid "This program is read-only, clone it to edit"
|
||||
msgstr ""
|
||||
|
||||
|
|
9
po/fr.po
9
po/fr.po
|
@ -346,6 +346,9 @@ msgstr "Console de triche\\Montre la console de triche"
|
|||
msgid "Checkpoint"
|
||||
msgstr "Indicateur"
|
||||
|
||||
msgid "Class name expected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Climb\\Increases the power of the jet"
|
||||
msgstr "Monter\\Augmenter la puissance du réacteur"
|
||||
|
||||
|
@ -921,6 +924,9 @@ msgstr "Pas d'uranium à transformer"
|
|||
msgid "No userlevels installed!"
|
||||
msgstr "Pas de niveaux spéciaux installés !"
|
||||
|
||||
msgid "Non-void function needs \"return;\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Normal size"
|
||||
msgstr "Taille normale"
|
||||
|
||||
|
@ -1488,6 +1494,9 @@ msgstr ""
|
|||
msgid "This object is not a member of a class"
|
||||
msgstr "L'objet n'est pas une instance d'une classe"
|
||||
|
||||
msgid "This parameter needs a default value"
|
||||
msgstr ""
|
||||
|
||||
msgid "This program is read-only, clone it to edit"
|
||||
msgstr "Ce programme est en lecture-seule, le dupliquer pour pouvoir l'éditer"
|
||||
|
||||
|
|
9
po/pl.po
9
po/pl.po
|
@ -348,6 +348,9 @@ msgstr "Konsola komend\\Pokaż konsolę komend"
|
|||
msgid "Checkpoint"
|
||||
msgstr "Punkt kontrolny"
|
||||
|
||||
msgid "Class name expected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Climb\\Increases the power of the jet"
|
||||
msgstr "W górę\\Zwiększa moc silnika"
|
||||
|
||||
|
@ -923,6 +926,9 @@ msgstr "Brak uranu do przetworzenia"
|
|||
msgid "No userlevels installed!"
|
||||
msgstr "Brak zainstalowanych poziomów użytkownika!"
|
||||
|
||||
msgid "Non-void function needs \"return;\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Normal size"
|
||||
msgstr "Normalna wielkość"
|
||||
|
||||
|
@ -1490,6 +1496,9 @@ msgstr "Ten objekt jest obecnie zajęty"
|
|||
msgid "This object is not a member of a class"
|
||||
msgstr "Ten obiekt nie jest członkiem klasy"
|
||||
|
||||
msgid "This parameter needs a default value"
|
||||
msgstr ""
|
||||
|
||||
msgid "This program is read-only, clone it to edit"
|
||||
msgstr "Ten program jest tylko do odczytu, skopiuj go, aby edytować"
|
||||
|
||||
|
|
9
po/ru.po
9
po/ru.po
|
@ -353,6 +353,9 @@ msgstr "Консоль чит-кодов\\Показать консоль для
|
|||
msgid "Checkpoint"
|
||||
msgstr "Контрольная точка"
|
||||
|
||||
msgid "Class name expected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Climb\\Increases the power of the jet"
|
||||
msgstr "Взлет и подъем\\Увеличивает мощность реактивного двигателя"
|
||||
|
||||
|
@ -932,6 +935,9 @@ msgstr "Нет урана для преобразования"
|
|||
msgid "No userlevels installed!"
|
||||
msgstr "Не установленны пользовательские уровни!"
|
||||
|
||||
msgid "Non-void function needs \"return;\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Normal size"
|
||||
msgstr "Нормальный размер"
|
||||
|
||||
|
@ -1504,6 +1510,9 @@ msgstr ""
|
|||
msgid "This object is not a member of a class"
|
||||
msgstr "Этот объект не член класса"
|
||||
|
||||
msgid "This parameter needs a default value"
|
||||
msgstr ""
|
||||
|
||||
msgid "This program is read-only, clone it to edit"
|
||||
msgstr "Эта программа только для чтения, для редактирования клонируйте её"
|
||||
|
||||
|
|
|
@ -471,26 +471,27 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
|
|||
|
||||
std::string name = p->GetString();
|
||||
|
||||
CBotClass* pOld = CBotClass::Find(name);
|
||||
if ( (pOld != nullptr && pOld->m_IsDef) || /* public class exists in different program */
|
||||
pStack->GetProgram()->ClassExists(name)) /* class exists in this program */
|
||||
{
|
||||
pStack->SetError( CBotErrRedefClass, p );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// a name of the class is there?
|
||||
if (IsOfType(p, TokenTypVar))
|
||||
{
|
||||
CBotClass* pOld = CBotClass::Find(name);
|
||||
if ((pOld != nullptr && pOld->m_IsDef) || /* public class exists in different program */
|
||||
pStack->GetProgram()->ClassExists(name)) /* class exists in this program */
|
||||
{
|
||||
pStack->SetError(CBotErrRedefClass, p->GetPrev());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CBotClass* pPapa = nullptr;
|
||||
if ( IsOfType( p, ID_EXTENDS ) )
|
||||
{
|
||||
std::string name = p->GetString();
|
||||
pPapa = CBotClass::Find(name);
|
||||
CBotToken* pp = p;
|
||||
|
||||
if (!IsOfType(p, TokenTypVar) || pPapa == nullptr )
|
||||
{
|
||||
pStack->SetError( CBotErrNotClass, p );
|
||||
pStack->SetError(CBotErrNoClassName, pp);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -519,6 +520,9 @@ CBotClass* CBotClass::Compile1(CBotToken* &p, CBotCStack* pStack)
|
|||
|
||||
if (pStack->IsOk()) return classe;
|
||||
}
|
||||
else
|
||||
pStack->SetError(CBotErrNoClassName, p);
|
||||
|
||||
pStack->SetError(CBotErrNoTerminator, p);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -810,10 +814,11 @@ CBotClass* CBotClass::Compile(CBotToken* &p, CBotCStack* pStack)
|
|||
// TODO: Not sure how correct is that - I have no idea how the precompilation (Compile1 method) works ~krzys_h
|
||||
std::string name = p->GetString();
|
||||
CBotClass* pPapa = CBotClass::Find(name);
|
||||
CBotToken* pp = p;
|
||||
|
||||
if (!IsOfType(p, TokenTypVar) || pPapa == nullptr)
|
||||
{
|
||||
pStack->SetError( CBotErrNotClass, p );
|
||||
pStack->SetError(CBotErrNoClassName, pp);
|
||||
return nullptr;
|
||||
}
|
||||
pOld->m_parent = pPapa;
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#include "CBot/CBotDefParam.h"
|
||||
|
||||
#include "CBot/CBotInstr/CBotInstrUtils.h"
|
||||
#include "CBot/CBotInstr/CBotParExpr.h"
|
||||
|
||||
#include "CBot/CBotUtils.h"
|
||||
#include "CBot/CBotCStack.h"
|
||||
|
||||
|
@ -33,11 +36,13 @@ namespace CBot
|
|||
CBotDefParam::CBotDefParam()
|
||||
{
|
||||
m_nIdent = 0;
|
||||
m_expr = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
CBotDefParam::~CBotDefParam()
|
||||
{
|
||||
delete m_expr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -51,8 +56,9 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack)
|
|||
if (IsOfType(p, ID_OPENPAR))
|
||||
{
|
||||
CBotDefParam* list = nullptr;
|
||||
bool prevHasDefault = false;
|
||||
|
||||
while (!IsOfType(p, ID_CLOSEPAR))
|
||||
if (!IsOfType(p, ID_CLOSEPAR)) while (true)
|
||||
{
|
||||
CBotDefParam* param = new CBotDefParam();
|
||||
if (list == nullptr) list = param;
|
||||
|
@ -77,6 +83,26 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack)
|
|||
break;
|
||||
}
|
||||
|
||||
if (IsOfType(p, ID_ASS)) // default value assignment
|
||||
{
|
||||
CBotCStack* pStk = pStack->TokenStack(nullptr, true);
|
||||
if (nullptr != (param->m_expr = CBotParExpr::CompileLitExpr(p, pStk)))
|
||||
{
|
||||
CBotTypResult valueType = pStk->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||
|
||||
if (!TypesCompatibles(type, valueType))
|
||||
pStack->SetError(CBotErrBadType1, p->GetPrev());
|
||||
|
||||
prevHasDefault = true;
|
||||
}
|
||||
else pStack->SetError(CBotErrNoExpression, p);
|
||||
delete pStk;
|
||||
}
|
||||
else
|
||||
if (prevHasDefault) pStack->SetError(CBotErrDefaultValue, p->GetPrev());
|
||||
|
||||
if (!pStack->IsOk()) break;
|
||||
|
||||
if ( type.Eq(CBotTypArrayPointer) ) type.SetType(CBotTypArrayBody);
|
||||
CBotVar* var = CBotVar::Create(pp->GetString(), type); // creates the variable
|
||||
// if ( pClass ) var->SetClass(pClass);
|
||||
|
@ -85,10 +111,12 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack)
|
|||
var->SetUniqNum(param->m_nIdent);
|
||||
pStack->AddVar(var); // place on the stack
|
||||
|
||||
if (IsOfType(p, ID_COMMA) || p->GetType() == ID_CLOSEPAR)
|
||||
continue;
|
||||
if (IsOfType(p, ID_COMMA)) continue;
|
||||
if (IsOfType(p, ID_CLOSEPAR)) break;
|
||||
|
||||
pStack->SetError(CBotErrClosePar, p->GetStart());
|
||||
}
|
||||
pStack->SetError(CBotErrClosePar, p->GetStart());
|
||||
pStack->SetError(CBotErrNoVar, p->GetStart());
|
||||
}
|
||||
pStack->SetError(CBotErrNoType, p);
|
||||
delete list;
|
||||
|
@ -107,40 +135,63 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj)
|
|||
assert(this != nullptr);
|
||||
CBotDefParam* p = this;
|
||||
|
||||
bool useDefault = false;
|
||||
|
||||
while ( p != nullptr )
|
||||
{
|
||||
// creates a local variable on the stack
|
||||
CBotVar* newvar = CBotVar::Create(p->m_token.GetString(), p->m_type);
|
||||
|
||||
CBotVar* pVar = nullptr;
|
||||
CBotStack* pile = nullptr; // stack for default expression
|
||||
|
||||
if (useDefault || (ppVars == nullptr || ppVars[i] == nullptr))
|
||||
{
|
||||
assert(p->m_expr != nullptr);
|
||||
|
||||
pile = pj->AddStack();
|
||||
useDefault = true;
|
||||
|
||||
while (pile->IsOk() && !p->m_expr->Execute(pile));
|
||||
if (!pile->IsOk()) return pj->Return(pile); // return the error
|
||||
|
||||
pVar = pile->GetVar();
|
||||
}
|
||||
else
|
||||
pVar = ppVars[i];
|
||||
|
||||
// serves to make the transformation of types:
|
||||
if ( ppVars != nullptr && ppVars[i] != nullptr )
|
||||
if ((useDefault && pVar != nullptr) ||
|
||||
(ppVars != nullptr && pVar != nullptr))
|
||||
{
|
||||
switch (p->m_type.GetType())
|
||||
{
|
||||
case CBotTypInt:
|
||||
newvar->SetValInt(ppVars[i]->GetValInt());
|
||||
newvar->SetValInt(pVar->GetValInt());
|
||||
newvar->SetInit(pVar->GetInit()); // copy nan
|
||||
break;
|
||||
case CBotTypFloat:
|
||||
newvar->SetValFloat(ppVars[i]->GetValFloat());
|
||||
newvar->SetValFloat(pVar->GetValFloat());
|
||||
newvar->SetInit(pVar->GetInit()); // copy nan
|
||||
break;
|
||||
case CBotTypString:
|
||||
newvar->SetValString(ppVars[i]->GetValString());
|
||||
newvar->SetValString(pVar->GetValString());
|
||||
break;
|
||||
case CBotTypBoolean:
|
||||
newvar->SetValInt(ppVars[i]->GetValInt());
|
||||
newvar->SetValInt(pVar->GetValInt());
|
||||
break;
|
||||
case CBotTypIntrinsic:
|
||||
(static_cast<CBotVarClass*>(newvar))->Copy(ppVars[i], false);
|
||||
(static_cast<CBotVarClass*>(newvar))->Copy(pVar, false);
|
||||
break;
|
||||
case CBotTypPointer:
|
||||
{
|
||||
newvar->SetPointer(ppVars[i]->GetPointer());
|
||||
newvar->SetPointer(pVar->GetPointer());
|
||||
newvar->SetType(p->m_type); // keep pointer type
|
||||
}
|
||||
break;
|
||||
case CBotTypArrayPointer:
|
||||
{
|
||||
newvar->SetPointer(ppVars[i]->GetPointer());
|
||||
newvar->SetPointer(pVar->GetPointer());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -150,12 +201,19 @@ bool CBotDefParam::Execute(CBotVar** ppVars, CBotStack* &pj)
|
|||
newvar->SetUniqNum(p->m_nIdent);
|
||||
pj->AddVar(newvar); // add a variable
|
||||
p = p->m_next;
|
||||
i++;
|
||||
if (!useDefault) i++;
|
||||
if (pile != nullptr) pile->Delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool CBotDefParam::HasDefault()
|
||||
{
|
||||
return (m_expr != nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void CBotDefParam::RestoreState(CBotStack* &pj, bool bMain)
|
||||
{
|
||||
|
|
|
@ -63,6 +63,12 @@ public:
|
|||
*/
|
||||
bool Execute(CBotVar** ppVars, CBotStack* &pj);
|
||||
|
||||
/*!
|
||||
* \brief Check if this parameter has a default value expression.
|
||||
* \return true if the parameter was compiled with a default value.
|
||||
*/
|
||||
bool HasDefault();
|
||||
|
||||
/*!
|
||||
* \brief RestoreState
|
||||
* \param pj
|
||||
|
@ -96,6 +102,9 @@ private:
|
|||
//! Type of paramteter.
|
||||
CBotTypResult m_type;
|
||||
long m_nIdent;
|
||||
|
||||
//! Default value expression for the parameter.
|
||||
CBotInstr* m_expr;
|
||||
};
|
||||
|
||||
} // namespace CBot
|
||||
|
|
|
@ -238,6 +238,9 @@ enum CBotError : int
|
|||
CBotErrNoExpression = 5043, //!< expression expected after =
|
||||
CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function
|
||||
CBotErrFuncNotVoid = 5045, //!< function needs return type "void"
|
||||
CBotErrNoClassName = 5046, //!< class name expected
|
||||
CBotErrNoReturn = 5047, //!< non-void function needs "return;"
|
||||
CBotErrDefaultValue = 5048, //!< this parameter needs a default value
|
||||
|
||||
// Runtime errors
|
||||
CBotErrZeroDiv = 6000, //!< division by zero
|
||||
|
|
|
@ -177,7 +177,11 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
|
|||
func->m_MasterClass = pp->GetString();
|
||||
func->m_classToken = *pp;
|
||||
CBotClass* pClass = CBotClass::Find(pp);
|
||||
if ( pClass == nullptr ) goto bad;
|
||||
if ( pClass == nullptr )
|
||||
{
|
||||
pStk->SetError(CBotErrNoClassName, pp);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
// pp = p;
|
||||
func->m_token = *p;
|
||||
|
@ -224,6 +228,12 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
|
|||
func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken();
|
||||
if ( pStk->IsOk() )
|
||||
{
|
||||
if (!func->m_retTyp.Eq(CBotTypVoid) && !func->HasReturn())
|
||||
{
|
||||
int errPos = func->m_closeblk.GetStart();
|
||||
pStk->ResetError(CBotErrNoReturn, errPos, errPos);
|
||||
goto bad;
|
||||
}
|
||||
return pStack->ReturnFunc(func, pStk);
|
||||
}
|
||||
}
|
||||
|
@ -280,13 +290,8 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas
|
|||
if ( IsOfType( p, ID_DBLDOTS ) ) // method for a class
|
||||
{
|
||||
func->m_MasterClass = pp->GetString();
|
||||
CBotClass* pClass = CBotClass::Find(pp);
|
||||
if ( pClass == nullptr )
|
||||
{
|
||||
pStk->SetError(CBotErrNotClass, pp);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
// existence of the class is checked
|
||||
// later in CBotFunction::Compile()
|
||||
pp = p;
|
||||
func->m_token = *p;
|
||||
if (!IsOfType(p, TokenTypVar)) goto bad;
|
||||
|
@ -498,8 +503,13 @@ CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& lo
|
|||
// parameters are compatible?
|
||||
CBotDefParam* pv = pt->m_param; // expected list of parameters
|
||||
CBotVar* pw = ppVars[i++]; // provided list parameter
|
||||
while ( pv != nullptr && pw != nullptr)
|
||||
while ( pv != nullptr && (pw != nullptr || pv->HasDefault()) )
|
||||
{
|
||||
if (pw == nullptr) // end of arguments
|
||||
{
|
||||
pv = pv->GetNext();
|
||||
continue; // skip params with default values
|
||||
}
|
||||
CBotTypResult paramType = pv->GetTypResult();
|
||||
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||
|
||||
|
@ -556,8 +566,13 @@ CBotFunction* CBotFunction::FindLocalOrPublic(const std::list<CBotFunction*>& lo
|
|||
// parameters sont-ils compatibles ?
|
||||
CBotDefParam* pv = pt->m_param; // list of expected parameters
|
||||
CBotVar* pw = ppVars[i++]; // list of provided parameters
|
||||
while ( pv != nullptr && pw != nullptr)
|
||||
while ( pv != nullptr && (pw != nullptr || pv->HasDefault()) )
|
||||
{
|
||||
if (pw == nullptr) // end of arguments
|
||||
{
|
||||
pv = pv->GetNext();
|
||||
continue; // skip params with default values
|
||||
}
|
||||
CBotTypResult paramType = pv->GetTypResult();
|
||||
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
||||
|
||||
|
@ -685,7 +700,14 @@ int CBotFunction::DoCall(CBotProgram* program, const std::list<CBotFunction*>& l
|
|||
// initializes the variables as parameters
|
||||
if (pt->m_param != nullptr)
|
||||
{
|
||||
pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted
|
||||
if (!pt->m_param->Execute(ppVars, pStk3)) // interupts only if error on a default value
|
||||
{
|
||||
if ( pt->m_pProg != program )
|
||||
{
|
||||
pStk3->SetPosError(pToken); // indicates the error on the procedure call
|
||||
}
|
||||
return pStack->Return(pStk3);
|
||||
}
|
||||
}
|
||||
|
||||
pStk1->IncState();
|
||||
|
@ -807,7 +829,14 @@ int CBotFunction::DoCall(const std::list<CBotFunction*>& localFunctionList, long
|
|||
// initializes the variables as parameters
|
||||
if (pt->m_param != nullptr)
|
||||
{
|
||||
pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted
|
||||
if (!pt->m_param->Execute(ppVars, pStk3)) // interupts only if error on a default value
|
||||
{
|
||||
if ( pt->m_pProg != pProgCurrent )
|
||||
{
|
||||
pStk3->SetPosError(pToken); // indicates the error on the procedure call
|
||||
}
|
||||
return pStack->Return(pStk3);
|
||||
}
|
||||
}
|
||||
pStk->IncState();
|
||||
}
|
||||
|
@ -939,6 +968,12 @@ void CBotFunction::AddPublic(CBotFunction* func)
|
|||
m_publicFunctions.insert(func);
|
||||
}
|
||||
|
||||
bool CBotFunction::HasReturn()
|
||||
{
|
||||
if (m_block != nullptr) return m_block->HasReturn();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string CBotFunction::GetDebugData()
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -235,6 +235,12 @@ public:
|
|||
CBotGet modestart,
|
||||
CBotGet modestop);
|
||||
|
||||
/*!
|
||||
* \brief Check if the function has a return statment that will execute.
|
||||
* \return true if a return statment was found.
|
||||
*/
|
||||
bool HasReturn() override;
|
||||
|
||||
protected:
|
||||
virtual const std::string GetDebugName() override { return "CBotFunction"; }
|
||||
virtual std::string GetDebugData() override;
|
||||
|
|
|
@ -163,6 +163,15 @@ void CBotIf :: RestoreState(CBotStack* &pj, bool bMain)
|
|||
}
|
||||
}
|
||||
|
||||
bool CBotIf::HasReturn()
|
||||
{
|
||||
if (m_block != nullptr && m_blockElse != nullptr)
|
||||
{
|
||||
if (m_block->HasReturn() && m_blockElse->HasReturn()) return true;
|
||||
}
|
||||
return CBotInstr::HasReturn(); // check next block or instruction
|
||||
}
|
||||
|
||||
std::map<std::string, CBotInstr*> CBotIf::GetDebugLinks()
|
||||
{
|
||||
auto links = CBotInstr::GetDebugLinks();
|
||||
|
|
|
@ -56,6 +56,14 @@ public:
|
|||
*/
|
||||
void RestoreState(CBotStack* &pj, bool bMain) override;
|
||||
|
||||
/**
|
||||
* \brief Check 'if' and 'else' for return statements.
|
||||
* Returns true when 'if' and 'else' have return statements,
|
||||
* if not, the next block or instruction is checked.
|
||||
* \return true if a return statement is found.
|
||||
*/
|
||||
bool HasReturn() override;
|
||||
|
||||
protected:
|
||||
virtual const std::string GetDebugName() override { return "CBotIf"; }
|
||||
virtual std::map<std::string, CBotInstr*> GetDebugLinks() override;
|
||||
|
|
|
@ -359,6 +359,13 @@ CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypRes
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool CBotInstr::HasReturn()
|
||||
{
|
||||
assert(this != nullptr);
|
||||
if (m_next != nullptr) return m_next->HasReturn();
|
||||
return false; // end of the list
|
||||
}
|
||||
|
||||
std::map<std::string, CBotInstr*> CBotInstr::GetDebugLinks()
|
||||
{
|
||||
return {
|
||||
|
|
|
@ -281,6 +281,12 @@ public:
|
|||
*/
|
||||
static bool ChkLvl(const std::string& label, int type);
|
||||
|
||||
/**
|
||||
* \brief Check a list of instructions for a return statement.
|
||||
* \return true if a return statement was found.
|
||||
*/
|
||||
virtual bool HasReturn();
|
||||
|
||||
protected:
|
||||
friend class CBotDebug;
|
||||
/**
|
||||
|
|
|
@ -52,7 +52,7 @@ CBotInstr* CBotListInstr::Compile(CBotToken* &p, CBotCStack* pStack, bool bLocal
|
|||
if (IsOfType(p, ID_SEP)) continue; // empty statement ignored
|
||||
if (p->GetType() == ID_CLBLK) break;
|
||||
|
||||
if (IsOfType(p, 0))
|
||||
if (p->GetType() == TokenTypNone)
|
||||
{
|
||||
pStack->SetError(CBotErrCloseBlock, p->GetStart());
|
||||
delete inst;
|
||||
|
@ -117,6 +117,12 @@ void CBotListInstr::RestoreState(CBotStack* &pj, bool bMain)
|
|||
if (p != nullptr) p->RestoreState(pile, true);
|
||||
}
|
||||
|
||||
bool CBotListInstr::HasReturn()
|
||||
{
|
||||
if (m_instr != nullptr && m_instr->HasReturn()) return true;
|
||||
return CBotInstr::HasReturn(); // check next block or instruction
|
||||
}
|
||||
|
||||
std::map<std::string, CBotInstr*> CBotListInstr::GetDebugLinks()
|
||||
{
|
||||
auto links = CBotInstr::GetDebugLinks();
|
||||
|
|
|
@ -56,6 +56,13 @@ public:
|
|||
*/
|
||||
void RestoreState(CBotStack* &pj, bool bMain) override;
|
||||
|
||||
/**
|
||||
* \brief Check this block of instructions for a return statement.
|
||||
* If not found, the next block or instruction is checked.
|
||||
* \return true if a return statement was found.
|
||||
*/
|
||||
bool HasReturn() override;
|
||||
|
||||
protected:
|
||||
virtual const std::string GetDebugName() override { return "CBotListInstr"; }
|
||||
virtual std::map<std::string, CBotInstr*> GetDebugLinks() override;
|
||||
|
|
|
@ -200,7 +200,7 @@ bool CBotNew::Execute(CBotStack* &pj)
|
|||
}
|
||||
ppVars[i] = nullptr;
|
||||
|
||||
if ( !pClass->ExecuteMethode(m_nMethodeIdent, pThis, ppVars, CBotTypResult(CBotTypVoid), pile2, GetToken())) return false; // interrupt
|
||||
if ( !pClass->ExecuteMethode(m_nMethodeIdent, pThis, ppVars, CBotTypResult(CBotTypVoid), pile2, &m_vartoken)) return false; // interrupt
|
||||
|
||||
pThis->ConstructorSet(); // indicates that the constructor has been called
|
||||
}
|
||||
|
|
|
@ -132,6 +132,16 @@ CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack)
|
|||
return pStack->Return(nullptr, pStk);
|
||||
}
|
||||
|
||||
return CompileLitExpr(p, pStack);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack)
|
||||
{
|
||||
CBotCStack* pStk = pStack->TokenStack();
|
||||
|
||||
CBotToken* pp = p;
|
||||
|
||||
// is it a number or DefineNum?
|
||||
if (p->GetType() == TokenTypNum ||
|
||||
p->GetType() == TokenTypDef )
|
||||
|
|
|
@ -54,6 +54,14 @@ public:
|
|||
*/
|
||||
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack);
|
||||
|
||||
/*!
|
||||
* \brief Compile a literal expression ("string", number, true, false, null, nan, new)
|
||||
* \param p[in, out] Pointer to first token of the expression, will be updated to point to first token after the expression
|
||||
* \param pStack Current compilation stack frame
|
||||
* \return The compiled instruction or nullptr on error
|
||||
*/
|
||||
static CBotInstr* CompileLitExpr(CBotToken* &p, CBotCStack* pStack);
|
||||
|
||||
private:
|
||||
CBotParExpr() = delete;
|
||||
CBotParExpr(const CBotParExpr&) = delete;
|
||||
|
|
|
@ -111,6 +111,11 @@ void CBotReturn::RestoreState(CBotStack* &pj, bool bMain)
|
|||
}
|
||||
}
|
||||
|
||||
bool CBotReturn::HasReturn()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, CBotInstr*> CBotReturn::GetDebugLinks()
|
||||
{
|
||||
auto links = CBotInstr::GetDebugLinks();
|
||||
|
|
|
@ -55,6 +55,12 @@ public:
|
|||
*/
|
||||
void RestoreState(CBotStack* &pj, bool bMain) override;
|
||||
|
||||
/*!
|
||||
* \brief Always returns true.
|
||||
* \return true to signal a return statment has been found.
|
||||
*/
|
||||
bool HasReturn() override;
|
||||
|
||||
protected:
|
||||
virtual const std::string GetDebugName() override { return "CBotReturn"; }
|
||||
virtual std::map<std::string, CBotInstr*> GetDebugLinks() override;
|
||||
|
|
|
@ -439,6 +439,13 @@ std::unique_ptr<CBotToken> CBotToken::CompileTokens(const std::string& program)
|
|||
pp = p;
|
||||
}
|
||||
|
||||
// terminator token
|
||||
nxt = new CBotToken();
|
||||
nxt->m_type = TokenTypNone;
|
||||
nxt->m_end = nxt->m_start = pos;
|
||||
prv->m_next = nxt;
|
||||
nxt->m_prev = prv;
|
||||
|
||||
return std::unique_ptr<CBotToken>(tokenbase);
|
||||
}
|
||||
|
||||
|
|
|
@ -720,6 +720,9 @@ void InitializeRestext()
|
|||
stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after =");
|
||||
stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function");
|
||||
stringsCbot[CBot::CBotErrFuncNotVoid] = TR("Function needs return type \"void\"");
|
||||
stringsCbot[CBot::CBotErrNoClassName] = TR("Class name expected");
|
||||
stringsCbot[CBot::CBotErrNoReturn] = TR("Non-void function needs \"return;\"");
|
||||
stringsCbot[CBot::CBotErrDefaultValue] = TR("This parameter needs a default value");
|
||||
|
||||
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
|
||||
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");
|
||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
|||
ASSERT_EQ(token->GetType(), correct.type) << "type mismatch at token #" << (i+1);
|
||||
i++;
|
||||
}
|
||||
while((token = token->GetNext()) != nullptr);
|
||||
while((token = token->GetNext()) != nullptr && !IsOfType(token, TokenTypNone));
|
||||
ASSERT_EQ(i, data.size()) << "not enough tokens processed";
|
||||
}
|
||||
};
|
||||
|
|
|
@ -298,6 +298,96 @@ TEST_F(CBotUT, EmptyTest)
|
|||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, FunctionCompileErrors)
|
||||
{
|
||||
ExecuteTest(
|
||||
"public",
|
||||
CBotErrNoType
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern",
|
||||
CBotErrNoType
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public void",
|
||||
CBotErrNoFunc
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void",
|
||||
CBotErrNoFunc
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void MissingParameterType(",
|
||||
CBotErrNoType
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void MissingParamName(int",
|
||||
CBotErrNoVar
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void MissingCloseParen(int i",
|
||||
CBotErrClosePar
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void ParamTrailingComma(int i, ) {\n"
|
||||
"}\n",
|
||||
CBotErrNoType
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void MissingOpenBlock(int i)",
|
||||
CBotErrOpenBlock
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void MissingCloseBlock()\n"
|
||||
"{\n",
|
||||
CBotErrCloseBlock
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, ClassCompileErrors)
|
||||
{
|
||||
ExecuteTest(
|
||||
"public class",
|
||||
CBotErrNoClassName
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public class 1234",
|
||||
CBotErrNoClassName
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public class TestClass",
|
||||
CBotErrOpenBlock
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public class TestClass\n"
|
||||
"{\n",
|
||||
CBotErrCloseBlock
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public class TestClass extends",
|
||||
CBotErrNoClassName
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"public class TestClass extends 1234",
|
||||
CBotErrNoClassName
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, DivideByZero)
|
||||
{
|
||||
ExecuteTest(
|
||||
|
@ -711,8 +801,7 @@ TEST_F(CBotUT, FunctionBadReturn)
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: Doesn't work
|
||||
TEST_F(CBotUT, DISABLED_FunctionNoReturn)
|
||||
TEST_F(CBotUT, FunctionNoReturn)
|
||||
{
|
||||
ExecuteTest(
|
||||
"int func()\n"
|
||||
|
@ -722,7 +811,49 @@ TEST_F(CBotUT, DISABLED_FunctionNoReturn)
|
|||
"{\n"
|
||||
" func();\n"
|
||||
"}\n",
|
||||
static_cast<CBotError>(-1) // TODO: no error for that
|
||||
CBotErrNoReturn
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"int FuncDoesNotReturnAValue()\n"
|
||||
"{\n"
|
||||
" if (false) return 1;\n"
|
||||
" while (false) return 1;\n"
|
||||
" if (true) ; else return 1;\n"
|
||||
" do { break; return 1; } while (false);\n"
|
||||
" do { continue; return 1; } while (false);\n"
|
||||
"}\n",
|
||||
CBotErrNoReturn
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"int FuncHasReturn()\n"
|
||||
"{\n"
|
||||
" return 1;\n"
|
||||
"}\n"
|
||||
"int BlockHasReturn()\n"
|
||||
"{\n"
|
||||
" {\n"
|
||||
" {\n"
|
||||
" }\n"
|
||||
" return 2;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"int IfElseHasReturn()\n"
|
||||
"{\n"
|
||||
" if (false) {\n"
|
||||
" return 3;\n"
|
||||
" } else {\n"
|
||||
" if (false) return 3;\n"
|
||||
" else return 3;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"extern void Test()\n"
|
||||
"{\n"
|
||||
" ASSERT(1 == FuncHasReturn());\n"
|
||||
" ASSERT(2 == BlockHasReturn());\n"
|
||||
" ASSERT(3 == IfElseHasReturn());\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1463,11 +1594,12 @@ TEST_F(CBotUT, StringFunctions)
|
|||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, DISABLED_TestNANParam_Issue642)
|
||||
TEST_F(CBotUT, TestNANParam_Issue642)
|
||||
{
|
||||
ExecuteTest(
|
||||
"float test(float x) {\n"
|
||||
" return x;\n"
|
||||
" ASSERT(x == nan);\n"
|
||||
" return x;\n"
|
||||
"}\n"
|
||||
"extern void TestNANParam() {\n"
|
||||
" ASSERT(nan == nan);\n" // TODO: Shouldn't it be nan != nan ??
|
||||
|
@ -2177,3 +2309,80 @@ TEST_F(CBotUT, IncrementDecrementSyntax)
|
|||
CBotErrBadType1
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, ParametersWithDefaultValues)
|
||||
{
|
||||
ExecuteTest(
|
||||
"extern void ParametersWithDefaultValues() {\n"
|
||||
" ASSERT(true == Test());\n"
|
||||
" ASSERT(true == Test(1));\n"
|
||||
" ASSERT(true == Test(1, 2));\n"
|
||||
"}\n"
|
||||
"bool Test(int i = 1, float f = 2.0) {\n"
|
||||
" return (i == 1) && (f == 2.0);\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void NotUsingDefaultValues() {\n"
|
||||
" ASSERT(true == Test(2, 4.0));\n"
|
||||
"}\n"
|
||||
"bool Test(int i = 1, float f = 2.0) {\n"
|
||||
" return (i == 2) && (f == 4.0);\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void NextParamNeedsDefaultValue() {\n"
|
||||
"}\n"
|
||||
"void Test(int i = 1, float f) {}\n"
|
||||
"\n",
|
||||
CBotErrDefaultValue
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void ParamMissingExpression() {\n"
|
||||
"}\n"
|
||||
"void Test(int i = 1, float f = ) {}\n"
|
||||
"\n",
|
||||
CBotErrNoExpression
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void ParamDefaultBadType() {\n"
|
||||
"}\n"
|
||||
"void Test(int i = 1, float f = null) {}\n"
|
||||
"\n",
|
||||
CBotErrBadType1
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void DefaultValuesAmbiguousCall() {\n"
|
||||
" Test();\n"
|
||||
"}\n"
|
||||
"void Test(int i = 1) {}\n"
|
||||
"void Test(float f = 2.0) {}\n"
|
||||
"\n",
|
||||
CBotErrAmbiguousCall
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void AmbiguousCallOneDefault() {\n"
|
||||
" Test(1);\n"
|
||||
"}\n"
|
||||
"void Test(int i, float f = 2) {}\n"
|
||||
"void Test(int i) {}\n"
|
||||
"\n",
|
||||
CBotErrAmbiguousCall
|
||||
);
|
||||
|
||||
ExecuteTest(
|
||||
"extern void DifferentNumberOfDefaultValues() {\n"
|
||||
" Test(1, 2.0);\n"
|
||||
"}\n"
|
||||
"void Test(int i, float f = 2.0) {}\n"
|
||||
"void Test(int i, float f = 2.0, int ii = 1) {}\n"
|
||||
"\n",
|
||||
CBotErrAmbiguousCall
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue