Add syntax for parameters with default values

Also fixes #642
dev-buzzingcars
melex750 2017-01-24 15:19:03 -05:00
parent baba6081b3
commit 92a8c48953
14 changed files with 218 additions and 16 deletions

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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"

View File

@ -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ć"

View File

@ -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 "Эта программа только для чтения, для редактирования клонируйте её"

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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
}

View File

@ -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 )

View File

@ -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;

View File

@ -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");

View File

@ -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
);
}