[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