[colobot] 46/100: Add syntax for parameters with default values

Didier Raboud odyx at moszumanska.debian.org
Thu Jun 1 18:10:17 UTC 2017


This is an automated email from the git hooks/post-receive script.

odyx pushed a commit to branch debian/master
in repository colobot.

commit 92a8c48953460a9b7aaf05e3bb3153f6451753a7
Author: melex750 <melex750 at users.noreply.github.com>
Date:   Tue Jan 24 15:19:03 2017 -0500

    Add syntax for parameters with default values
    
    Also fixes #642
---
 po/colobot.pot                      |  3 ++
 po/de.po                            |  3 ++
 po/fr.po                            |  3 ++
 po/pl.po                            |  3 ++
 po/ru.po                            |  3 ++
 src/CBot/CBotDefParam.cpp           | 74 +++++++++++++++++++++++++++++----
 src/CBot/CBotDefParam.h             |  9 ++++
 src/CBot/CBotEnums.h                |  1 +
 src/CBot/CBotInstr/CBotFunction.cpp | 32 +++++++++++++--
 src/CBot/CBotInstr/CBotNew.cpp      |  2 +-
 src/CBot/CBotInstr/CBotParExpr.cpp  | 10 +++++
 src/CBot/CBotInstr/CBotParExpr.h    |  8 ++++
 src/common/restext.cpp              |  1 +
 test/unit/CBot/CBot_test.cpp        | 82 ++++++++++++++++++++++++++++++++++++-
 14 files changed, 218 insertions(+), 16 deletions(-)

diff --git a/po/colobot.pot b/po/colobot.pot
index 72b447a..30a4c1c 100644
--- a/po/colobot.pot
+++ b/po/colobot.pot
@@ -1745,6 +1745,9 @@ msgstr ""
 msgid "Non-void function needs \"return;\""
 msgstr ""
 
+msgid "This parameter needs a default value"
+msgstr ""
+
 msgid "Dividing by zero"
 msgstr ""
 
diff --git a/po/de.po b/po/de.po
index cdcc3f3..255608e 100644
--- a/po/de.po
+++ b/po/de.po
@@ -1522,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 ""
 
diff --git a/po/fr.po b/po/fr.po
index 71b45fb..818948a 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1494,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"
 
diff --git a/po/pl.po b/po/pl.po
index 19b0f2b..aaa50b8 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -1496,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ć"
 
diff --git a/po/ru.po b/po/ru.po
index c8ed604..f479b47 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -1510,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 "Эта программа только для чтения, для редактирования клонируйте её"
 
diff --git a/src/CBot/CBotDefParam.cpp b/src/CBot/CBotDefParam.cpp
index e66c862..8b54f5a 100644
--- a/src/CBot/CBotDefParam.cpp
+++ b/src/CBot/CBotDefParam.cpp
@@ -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,6 +56,7 @@ CBotDefParam* CBotDefParam::Compile(CBotToken* &p, CBotCStack* pStack)
     if (IsOfType(p, ID_OPENPAR))
     {
         CBotDefParam* list = nullptr;
+        bool prevHasDefault = false;
 
         if (!IsOfType(p, ID_CLOSEPAR)) while (true)
         {
@@ -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);
@@ -109,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:
@@ -152,13 +201,20 @@ 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)
 {
 //    int             i = 0;
diff --git a/src/CBot/CBotDefParam.h b/src/CBot/CBotDefParam.h
index c23855d..bc9d9d0 100644
--- a/src/CBot/CBotDefParam.h
+++ b/src/CBot/CBotDefParam.h
@@ -64,6 +64,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
      * \param bMain
@@ -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
diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h
index 22022c7..f98d4e9 100644
--- a/src/CBot/CBotEnums.h
+++ b/src/CBot/CBotEnums.h
@@ -240,6 +240,7 @@ enum CBotError : int
     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
diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp
index 78d55fb..0f6a980 100644
--- a/src/CBot/CBotInstr/CBotFunction.cpp
+++ b/src/CBot/CBotInstr/CBotFunction.cpp
@@ -503,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);
 
@@ -561,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);
 
@@ -690,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();
@@ -812,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();
         }
diff --git a/src/CBot/CBotInstr/CBotNew.cpp b/src/CBot/CBotInstr/CBotNew.cpp
index 9fab382..1c84dc5 100644
--- a/src/CBot/CBotInstr/CBotNew.cpp
+++ b/src/CBot/CBotInstr/CBotNew.cpp
@@ -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
     }
diff --git a/src/CBot/CBotInstr/CBotParExpr.cpp b/src/CBot/CBotInstr/CBotParExpr.cpp
index a32363f..b0c2f1c 100644
--- a/src/CBot/CBotInstr/CBotParExpr.cpp
+++ b/src/CBot/CBotInstr/CBotParExpr.cpp
@@ -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 )
diff --git a/src/CBot/CBotInstr/CBotParExpr.h b/src/CBot/CBotInstr/CBotParExpr.h
index cd92467..0bcdd0e 100644
--- a/src/CBot/CBotInstr/CBotParExpr.h
+++ b/src/CBot/CBotInstr/CBotParExpr.h
@@ -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;
diff --git a/src/common/restext.cpp b/src/common/restext.cpp
index 5d1f012..035389b 100644
--- a/src/common/restext.cpp
+++ b/src/common/restext.cpp
@@ -722,6 +722,7 @@ void InitializeRestext()
     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");
diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp
index 1d02b8a..2f8bb50 100644
--- a/test/unit/CBot/CBot_test.cpp
+++ b/test/unit/CBot/CBot_test.cpp
@@ -1594,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 ??
@@ -2308,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
+    );
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/colobot.git



More information about the Pkg-games-commits mailing list