Fix constructor/destructor and field syntax

dev-buzzingcars
melex750 2017-01-16 12:34:18 -05:00 committed by krzys_h
parent 8fc0151444
commit 64bc1f1afb
11 changed files with 167 additions and 33 deletions

View File

@ -1736,6 +1736,9 @@ msgstr ""
msgid "Ambiguous call to overloaded function" msgid "Ambiguous call to overloaded function"
msgstr "" msgstr ""
msgid "Function needs return type \"void\""
msgstr ""
msgid "Dividing by zero" msgid "Dividing by zero"
msgstr "" msgstr ""

View File

@ -616,6 +616,9 @@ msgstr "Diese Funktion gibt es schon"
msgid "Function name missing" msgid "Function name missing"
msgstr "Hier muss der Name der Funktion stehen" msgstr "Hier muss der Name der Funktion stehen"
msgid "Function needs return type \"void\""
msgstr ""
msgid "Game speed" msgid "Game speed"
msgstr "Spielgeschwindigkeit" msgstr "Spielgeschwindigkeit"

View File

@ -603,6 +603,9 @@ msgstr "Cette fonction existe déjà"
msgid "Function name missing" msgid "Function name missing"
msgstr "Nom de la fonction attendu" msgstr "Nom de la fonction attendu"
msgid "Function needs return type \"void\""
msgstr ""
msgid "Game speed" msgid "Game speed"
msgstr "Vitesse du jeu" msgstr "Vitesse du jeu"

View File

@ -605,6 +605,9 @@ msgstr "Funkcja już istnieje"
msgid "Function name missing" msgid "Function name missing"
msgstr "Brakująca nazwa funkcji" msgstr "Brakująca nazwa funkcji"
msgid "Function needs return type \"void\""
msgstr ""
msgid "Game speed" msgid "Game speed"
msgstr "Prędkość gry" msgstr "Prędkość gry"

View File

@ -612,6 +612,9 @@ msgstr "Функция уже существует"
msgid "Function name missing" msgid "Function name missing"
msgstr "Имя функции отсутствует" msgstr "Имя функции отсутствует"
msgid "Function needs return type \"void\""
msgstr ""
msgid "Game speed" msgid "Game speed"
msgstr "Скорость игры" msgstr "Скорость игры"

View File

@ -248,6 +248,19 @@ CBotVar* CBotClass::GetItemRef(int nIdent)
return nullptr; return nullptr;
} }
////////////////////////////////////////////////////////////////////////////////
bool CBotClass::CheckVar(const std::string &name)
{
CBotVar* p = m_pVar;
while ( p != nullptr )
{
if ( p->GetName() == name ) return true;
p = p->GetNext();
}
return false;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool CBotClass::IsIntrinsic() bool CBotClass::IsIntrinsic()
{ {
@ -556,6 +569,8 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
while (pStack->IsOk()) while (pStack->IsOk())
{ {
CBotTypResult type2 = CBotTypResult(type); // reset type after comma CBotTypResult type2 = CBotTypResult(type); // reset type after comma
CBotToken* varToken = p;
std::string pp = p->GetString(); std::string pp = p->GetString();
if ( IsOfType(p, ID_NOT) ) if ( IsOfType(p, ID_NOT) )
{ {
@ -564,33 +579,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
if (IsOfType(p, TokenTypVar)) if (IsOfType(p, TokenTypVar))
{ {
CBotInstr* limites = nullptr;
while ( IsOfType( p, ID_OPBRK ) ) // a table?
{
CBotInstr* i = nullptr;
pStack->SetStartError( p->GetStart() );
if ( p->GetType() != ID_CLBRK )
{
i = CBotExpression::Compile( p, pStack ); // expression for the value
if (i == nullptr || pStack->GetType() != CBotTypInt) // must be a number
{
pStack->SetError(CBotErrBadIndex, p->GetStart());
return false;
}
}
else
i = new CBotEmpty(); // special if not a formula
type2 = CBotTypResult(CBotTypArrayPointer, type2);
if (limites == nullptr) limites = i;
else limites->AddNext3(i);
if (IsOfType(p, ID_CLBRK)) continue;
pStack->SetError(CBotErrCloseIndex, p->GetStart());
return false;
}
if ( p->GetType() == ID_OPENPAR ) if ( p->GetType() == ID_OPENPAR )
{ {
if ( !bSecond ) if ( !bSecond )
@ -673,12 +661,51 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond)
} }
// definition of an element // definition of an element
if (type2.Eq(0)) if (type.Eq(0))
{ {
pStack->SetError(CBotErrNoTerminator, p); pStack->SetError(CBotErrNoTerminator, p);
return false; return false;
} }
if (pp[0] == '~' || pp == GetName()) // bad variable name
{
pStack->SetError(CBotErrNoVar, varToken);
return false;
}
if (!bSecond && CheckVar(pp)) // variable already exists
{
pStack->SetError(CBotErrRedefVar, varToken);
return false;
}
CBotInstr* limites = nullptr;
while ( IsOfType( p, ID_OPBRK ) ) // an array
{
CBotInstr* i = nullptr;
pStack->SetStartError( p->GetStart() );
if ( p->GetType() != ID_CLBRK )
{
i = CBotExpression::Compile( p, pStack ); // expression for the value
if (i == nullptr || pStack->GetType() != CBotTypInt) // must be a number
{
pStack->SetError(CBotErrBadIndex, p->GetStart());
return false;
}
}
else
i = new CBotEmpty(); // special if not a formula
type2 = CBotTypResult(CBotTypArrayPointer, type2);
if (limites == nullptr) limites = i;
else limites->AddNext3(i);
if (IsOfType(p, ID_CLBRK)) continue;
pStack->SetError(CBotErrCloseIndex, p->GetStart());
return false;
}
CBotInstr* i = nullptr; CBotInstr* i = nullptr;
if ( IsOfType(p, ID_ASS ) ) if ( IsOfType(p, ID_ASS ) )
{ {

View File

@ -221,6 +221,13 @@ public:
*/ */
CBotVar* GetItemRef(int nIdent); CBotVar* GetItemRef(int nIdent);
/*!
* \brief Check whether a variable is already defined in a class
* \param name Name of the variable
* \return True if a variable is defined in the class
*/
bool CheckVar(const std::string &name);
/*! /*!
* \brief CompileMethode Compiles a method associated with an instance of * \brief CompileMethode Compiles a method associated with an instance of
* class the method can be declared by the user or AddFunction. * class the method can be declared by the user or AddFunction.

View File

@ -237,6 +237,7 @@ enum CBotError : int
CBotErrNoPublic = 5042, //!< missing word "public" CBotErrNoPublic = 5042, //!< missing word "public"
CBotErrNoExpression = 5043, //!< expression expected after = CBotErrNoExpression = 5043, //!< expression expected after =
CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function
CBotErrFuncNotVoid = 5045, //!< function needs return type "void"
// Runtime errors // Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -165,6 +165,7 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct
if ( IsOfType(p, ID_NOT) ) if ( IsOfType(p, ID_NOT) )
{ {
CBotToken d(std::string("~") + p->GetString()); CBotToken d(std::string("~") + p->GetString());
d.SetPos(pp->GetStart(), p->GetEnd());
func->m_token = d; func->m_token = d;
} }
@ -268,6 +269,7 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas
if ( IsOfType(p, ID_NOT) ) if ( IsOfType(p, ID_NOT) )
{ {
CBotToken d(std::string("~") + p->GetString()); CBotToken d(std::string("~") + p->GetString());
d.SetPos(pp->GetStart(), p->GetEnd());
func->m_token = d; func->m_token = d;
} }
@ -289,10 +291,40 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas
if (!IsOfType(p, TokenTypVar)) goto bad; if (!IsOfType(p, TokenTypVar)) goto bad;
} }
func->m_param = CBotDefParam::Compile(p, pStk );
CBotToken* openPar = p;
func->m_param = CBotDefParam::Compile(p, pStk); // compile parameters
if (pStk->IsOk() && pClass != nullptr) // method in a class
{
// check if a constructor has return type void
if (func->GetName() == pClass->GetName() && !func->m_retTyp.Eq(CBotTypVoid))
{
pp = &(func->m_retToken);
pStk->SetError(CBotErrFuncNotVoid, pp);
}
if (pStk->IsOk() && pp->GetString() == "~") // destructor
{
// check destructor name
if (func->GetName() != ("~" + pClass->GetName()))
pStk->SetError(CBotErrNoFunc, pp);
// confirm no parameters
if (pStk->IsOk() && func->m_param != nullptr)
pStk->SetError(CBotErrClosePar, openPar->GetNext());
// must return void
if (pStk->IsOk() && !func->m_retTyp.Eq(CBotTypVoid))
{
pp = &(func->m_retToken);
pStk->SetError(CBotErrFuncNotVoid, pp);
}
}
}
if (pStk->IsOk()) if (pStk->IsOk())
{ {
// looks if the function exists elsewhere // looks if the function exists elsewhere
pp = &(func->m_token);
if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) && if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) &&
( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) ) ( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) )
{ {

View File

@ -719,6 +719,7 @@ void InitializeRestext()
stringsCbot[CBot::CBotErrNoPublic] = TR("Public required"); stringsCbot[CBot::CBotErrNoPublic] = TR("Public required");
stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after ="); stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after =");
stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function"); stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function");
stringsCbot[CBot::CBotErrFuncNotVoid] = TR("Function needs return type \"void\"");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -867,14 +867,13 @@ TEST_F(CBotUT, ClassNullPointer)
); );
} }
// TODO: This doesn't work TEST_F(CBotUT, ClassDestructorNaming)
TEST_F(CBotUT, DISABLED_ClassDestructorNaming)
{ {
ExecuteTest( ExecuteTest(
"public class TestClass {\n" "public class TestClass {\n"
" public void ~SomethingElse() {}\n" " public void ~SomethingElse() {}\n"
"}\n", "}\n",
static_cast<CBotError>(-1) // TODO: no error for that CBotErrNoFunc
); );
ExecuteTest( ExecuteTest(
"public class SomethingElse {\n" "public class SomethingElse {\n"
@ -882,7 +881,26 @@ TEST_F(CBotUT, DISABLED_ClassDestructorNaming)
"public class TestClass2 {\n" "public class TestClass2 {\n"
" public void ~SomethingElse() {}\n" " public void ~SomethingElse() {}\n"
"}\n", "}\n",
static_cast<CBotError>(-1) // TODO: no error for that CBotErrNoFunc
);
}
TEST_F(CBotUT, ClassDestructorSyntax)
{
ExecuteTest(
"public class TestClass {\n"
" void ~TestClass(int i) {}\n"
"}\n"
"extern void DestructorNoParams() {}\n",
CBotErrClosePar
);
ExecuteTest(
"public class TestClass {\n"
" int ~TestClass() {}\n"
"}\n"
"extern void DestructorReturnTypeVoid() {}\n",
CBotErrFuncNotVoid
); );
} }
@ -930,6 +948,39 @@ TEST_F(CBotUT, ClassMethodRedefined)
"}\n", "}\n",
CBotErrRedefFunc CBotErrRedefFunc
); );
ExecuteTest(
"public class TestClass {\n"
" void ~TestClass() {}\n"
" void ~TestClass() {}\n"
"}\n",
CBotErrRedefFunc
);
}
TEST_F(CBotUT, ClassFieldNaming)
{
ExecuteTest(
"public class TestClass {\n"
" int ~i = 1;\n"
"}\n",
CBotErrNoVar
);
ExecuteTest(
"public class TestClass {\n"
" int TestClass = 1;\n"
"}\n",
CBotErrNoVar
);
ExecuteTest(
"public class TestClass {\n"
" int i = 1;\n"
" int i = 2;\n"
"}\n",
CBotErrRedefVar
);
} }
TEST_F(CBotUT, ClassRedefinedInDifferentPrograms) TEST_F(CBotUT, ClassRedefinedInDifferentPrograms)