parent
baba6081b3
commit
92a8c48953
|
@ -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 ""
|
||||
|
||||
|
|
3
po/de.po
3
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 ""
|
||||
|
||||
|
|
3
po/fr.po
3
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"
|
||||
|
||||
|
|
3
po/pl.po
3
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ć"
|
||||
|
||||
|
|
3
po/ru.po
3
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 "Эта программа только для чтения, для редактирования клонируйте её"
|
||||
|
||||
|
|
|
@ -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,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue