From 64bc1f1afb0eb00cbd403fe1fbc21bf2e4b84a74 Mon Sep 17 00:00:00 2001 From: melex750 Date: Mon, 16 Jan 2017 12:34:18 -0500 Subject: [PATCH] Fix constructor/destructor and field syntax --- po/colobot.pot | 3 ++ po/de.po | 3 ++ po/fr.po | 3 ++ po/pl.po | 3 ++ po/ru.po | 3 ++ src/CBot/CBotClass.cpp | 83 +++++++++++++++++++---------- src/CBot/CBotClass.h | 7 +++ src/CBot/CBotEnums.h | 1 + src/CBot/CBotInstr/CBotFunction.cpp | 34 +++++++++++- src/common/restext.cpp | 1 + test/unit/CBot/CBot_test.cpp | 59 ++++++++++++++++++-- 11 files changed, 167 insertions(+), 33 deletions(-) diff --git a/po/colobot.pot b/po/colobot.pot index a1446763..fd5c3de4 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -1736,6 +1736,9 @@ msgstr "" msgid "Ambiguous call to overloaded function" msgstr "" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index 653fa662..e4c105af 100644 --- a/po/de.po +++ b/po/de.po @@ -616,6 +616,9 @@ msgstr "Diese Funktion gibt es schon" msgid "Function name missing" msgstr "Hier muss der Name der Funktion stehen" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Spielgeschwindigkeit" diff --git a/po/fr.po b/po/fr.po index 6f432671..905a0163 100644 --- a/po/fr.po +++ b/po/fr.po @@ -603,6 +603,9 @@ msgstr "Cette fonction existe déjà" msgid "Function name missing" msgstr "Nom de la fonction attendu" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Vitesse du jeu" diff --git a/po/pl.po b/po/pl.po index f17c8176..cd9f37c8 100644 --- a/po/pl.po +++ b/po/pl.po @@ -605,6 +605,9 @@ msgstr "Funkcja już istnieje" msgid "Function name missing" msgstr "Brakująca nazwa funkcji" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Prędkość gry" diff --git a/po/ru.po b/po/ru.po index 70727694..233cb300 100644 --- a/po/ru.po +++ b/po/ru.po @@ -612,6 +612,9 @@ msgstr "Функция уже существует" msgid "Function name missing" msgstr "Имя функции отсутствует" +msgid "Function needs return type \"void\"" +msgstr "" + msgid "Game speed" msgstr "Скорость игры" diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index 13fb8379..127d6d34 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -248,6 +248,19 @@ CBotVar* CBotClass::GetItemRef(int nIdent) 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() { @@ -556,6 +569,8 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) while (pStack->IsOk()) { CBotTypResult type2 = CBotTypResult(type); // reset type after comma + CBotToken* varToken = p; + std::string pp = p->GetString(); if ( IsOfType(p, ID_NOT) ) { @@ -564,33 +579,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) 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 ( !bSecond ) @@ -673,12 +661,51 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) } // definition of an element - if (type2.Eq(0)) + if (type.Eq(0)) { pStack->SetError(CBotErrNoTerminator, p); 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; if ( IsOfType(p, ID_ASS ) ) { diff --git a/src/CBot/CBotClass.h b/src/CBot/CBotClass.h index 68b61cc6..503c62db 100644 --- a/src/CBot/CBotClass.h +++ b/src/CBot/CBotClass.h @@ -221,6 +221,13 @@ public: */ 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 * class the method can be declared by the user or AddFunction. diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index da0dbea7..b7453e59 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -237,6 +237,7 @@ enum CBotError : int CBotErrNoPublic = 5042, //!< missing word "public" CBotErrNoExpression = 5043, //!< expression expected after = CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function + CBotErrFuncNotVoid = 5045, //!< function needs return type "void" // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index f86153f5..7852c7ef 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -165,6 +165,7 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct if ( IsOfType(p, ID_NOT) ) { CBotToken d(std::string("~") + p->GetString()); + d.SetPos(pp->GetStart(), p->GetEnd()); func->m_token = d; } @@ -268,6 +269,7 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas if ( IsOfType(p, ID_NOT) ) { CBotToken d(std::string("~") + p->GetString()); + d.SetPos(pp->GetStart(), p->GetEnd()); func->m_token = d; } @@ -289,10 +291,40 @@ CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClas 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()) { // looks if the function exists elsewhere + pp = &(func->m_token); if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) && ( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) ) { diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 5cd6c91a..789c68a4 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -719,6 +719,7 @@ void InitializeRestext() stringsCbot[CBot::CBotErrNoPublic] = TR("Public required"); 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::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index e456261b..ca9ff919 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -867,14 +867,13 @@ TEST_F(CBotUT, ClassNullPointer) ); } -// TODO: This doesn't work -TEST_F(CBotUT, DISABLED_ClassDestructorNaming) +TEST_F(CBotUT, ClassDestructorNaming) { ExecuteTest( "public class TestClass {\n" " public void ~SomethingElse() {}\n" "}\n", - static_cast(-1) // TODO: no error for that + CBotErrNoFunc ); ExecuteTest( "public class SomethingElse {\n" @@ -882,7 +881,26 @@ TEST_F(CBotUT, DISABLED_ClassDestructorNaming) "public class TestClass2 {\n" " public void ~SomethingElse() {}\n" "}\n", - static_cast(-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", 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)