Add method chaining for class constructor calls

dev-new-models
melex750 2016-08-14 16:56:17 -04:00
parent d411c5ebc0
commit 3146d4ef35
9 changed files with 143 additions and 13 deletions

View File

@ -19,6 +19,7 @@
#include "CBot/CBotInstr/CBotDefClass.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotInstr/CBotLeftExprVar.h"
@ -44,6 +45,7 @@ CBotDefClass::CBotDefClass()
m_expr = nullptr;
m_hasParams = false;
m_nMethodeIdent = 0;
m_exprRetVar = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
@ -150,6 +152,16 @@ CBotInstr* CBotDefClass::Compile(CBotToken* &p, CBotCStack* pStack, CBotClass* p
goto error;
}
pStk->SetCopyVar(var);
// chained method ?
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true)))
{
inst->m_exprRetVar->SetToken(vartoken);
delete pStk->TokenStack();
}
pStk->SetVar(nullptr);
if ( !pStk->IsOk() ) goto error;
}
if (IsOfType(p, ID_ASS)) // with a assignment?
@ -232,6 +244,19 @@ bool CBotDefClass::Execute(CBotStack* &pj)
CBotStack* pile = pj->AddStack(this);//essential for SetState()
// if ( pile == EOX ) return true;
if (m_exprRetVar != nullptr) // Class c().method();
{
if (pile->IfStep()) return false;
if (pile->GetState() == 4)
{
CBotStack* pile3 = pile->AddStack();
if (!m_exprRetVar->Execute(pile3)) return false;
pile3->SetVar(nullptr);
pile->Return(pile3); // release pile3 stack
pile->SetState(5);
}
}
CBotToken* pt = &m_token;
CBotClass* pClass = CBotClass::Find(pt);
@ -363,6 +388,14 @@ bool CBotDefClass::Execute(CBotStack* &pj)
pile->SetState(3); // finished this part
}
if (m_exprRetVar != nullptr && pile->GetState() == 3) // Class c().method();
{
CBotStack* pile3 = pile->AddStack();
pile3->SetCopyVar(pThis);
pile->SetState(4);
return false; // go back to the top ^^^
}
if ( pile->IfStep() ) return false;
if ( m_next2b != nullptr &&
@ -387,6 +420,16 @@ void CBotDefClass::RestoreState(CBotStack* &pj, bool bMain)
pThis->SetUniqNum((static_cast<CBotLeftExprVar*>(m_var))->m_nIdent); // its attribute a unique number
}
if (m_exprRetVar != nullptr) // Class c().method();
{
if (pile->GetState() == 4)
{
CBotStack* pile3 = pile->RestoreStack();
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotToken* pt = &m_token;
CBotClass* pClass = CBotClass::Find(pt);
bool bIntrincic = pClass->IsIntrinsic();

View File

@ -85,6 +85,9 @@ private:
//! Constructor method unique identifier
long m_nMethodeIdent;
//! Instruction to chain method calls after constructor
CBotInstr* m_exprRetVar;
};
} // namespace CBot

View File

@ -41,17 +41,13 @@ CBotExprRetVar::~CBotExprRetVar()
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack)
CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly)
{
if (p->GetType() == ID_DOT)
{
CBotVar* var = pStack->GetVar();
if (var == nullptr)
{
pStack->SetError(CBotErrNoTerminator, p->GetStart());
return nullptr;
}
if (var == nullptr) return nullptr;
CBotCStack* pStk = pStack->TokenStack();
CBotInstr* inst = new CBotExprRetVar();
@ -61,6 +57,8 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack)
pStk->SetStartError(p->GetStart());
if (var->GetType() == CBotTypArrayPointer)
{
if (bMethodsOnly) goto err;
if (IsOfType( p, ID_OPBRK ))
{
CBotIndexExpr* i = new CBotIndexExpr();
@ -92,11 +90,16 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack)
{
if (p->GetNext()->GetType() == ID_OPENPAR)
{
CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var);
CBotInstr* i = CBotInstrMethode::Compile(p, pStk, var, bMethodsOnly);
if (!pStk->IsOk()) goto err;
inst->AddNext3(i);
return pStack->Return(inst, pStk);
}
else if (bMethodsOnly)
{
p = p->GetPrev();
goto err;
}
else
{
CBotFieldExpr* i = new CBotFieldExpr();

View File

@ -36,7 +36,7 @@ public:
CBotExprRetVar();
~CBotExprRetVar();
static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack);
static CBotInstr* Compile(CBotToken*& p, CBotCStack* pStack, bool bMethodsOnly = false);
/*!
* \brief Execute

View File

@ -46,7 +46,7 @@ CBotInstrMethode::~CBotInstrMethode()
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var)
CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var, bool bMethodChain)
{
CBotInstrMethode* inst = new CBotInstrMethode();
inst->SetToken(p); // corresponding token
@ -90,9 +90,10 @@ CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar*
}
else pStack->SetVar(nullptr);
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack)))
pp = p;
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack, bMethodChain)))
{
inst->m_exprRetVar->SetToken(&inst->m_token);
inst->m_exprRetVar->SetToken(pp);
delete pStack->TokenStack();
}

View File

@ -38,9 +38,10 @@ public:
* \param p
* \param pStack
* \param pVar
* \param bMethodChain If true, allows chaining methods only
* \return
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* pVar, bool bMethodChain = false);
/*!
* \brief Execute

View File

@ -24,6 +24,7 @@
#include "CBot/CBotCStack.h"
#include "CBot/CBotClass.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotVar/CBotVar.h"
@ -105,7 +106,17 @@ CBotInstr* CBotNew::Compile(CBotToken* &p, CBotCStack* pStack)
// makes pointer to the object on the stack
pStk->SetVar(pVar);
return pStack->Return(inst, pStk);
pp = p;
// chained method ?
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStk, true)))
{
inst->m_exprRetVar->SetToken(pp);
delete pStk->TokenStack();
}
if (pStack->IsOk())
return pStack->Return(inst, pStk);
}
error:
delete inst;
@ -117,6 +128,16 @@ bool CBotNew::Execute(CBotStack* &pj)
{
CBotStack* pile = pj->AddStack(this); //main stack
if (m_exprRetVar != nullptr) // new Class().method()
{
if (pile->GetState() == 2)
{
CBotStack* pile3 = pile->AddStack();
if (!m_exprRetVar->Execute(pile3)) return false;
return pj->Return(pile3);
}
}
if (pile->IfStep()) return false;
CBotStack* pile1 = pj->AddStack2(); //secondary stack
@ -186,6 +207,16 @@ bool CBotNew::Execute(CBotStack* &pj)
pThis->ConstructorSet(); // indicates that the constructor has been called
}
if (m_exprRetVar != nullptr) // new Class().method()
{
pile->AddStack()->Delete(); // release pile2 stack
CBotStack* pile3 = pile->AddStack(); // add new stack
pile3->SetCopyVar(pThis); // copy the pointer (from pile1)
pile1->Delete(); // release secondary stack(pile1)
pile->SetState(2);
return false; // go back to the top ^^^
}
return pj->Return(pile1); // passes below
}
@ -197,6 +228,16 @@ void CBotNew::RestoreState(CBotStack* &pj, bool bMain)
CBotStack* pile = pj->RestoreStack(this); //primary stack
if (pile == nullptr) return;
if (m_exprRetVar != nullptr) // new Class().method()
{
if (pile->GetState() == 2)
{
CBotStack* pile3 = pile->RestoreStack();
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotStack* pile1 = pj->AddStack2(); //secondary stack
CBotToken* pt = &m_vartoken;

View File

@ -66,6 +66,9 @@ private:
long m_nMethodeIdent;
CBotToken m_vartoken;
//! Instruction to chain method calls after constructor
CBotInstr* m_exprRetVar;
};
} // namespace CBot

View File

@ -1751,3 +1751,38 @@ TEST_F(CBotUT, InstrCallAccessMemberNewObjectDestructor)
"}\n"
);
}
TEST_F(CBotUT, ClassConstructorMethodChain)
{
ExecuteTest(
"public class TestClass {\n"
" int a = 123;\n"
" int b = 246;\n"
" TestClass testSetA(int x) { a = x; return this; }\n"
" TestClass testSetB(int y) { b = y; return this; }\n"
"}\n"
"extern void ConstructorMethodChain() {\n"
" TestClass tc().testSetA(111).testSetB(222);\n"
" ASSERT(tc.a == 111);\n"
" ASSERT(tc.b == 222);\n"
"}\n"
);
}
TEST_F(CBotUT, ClassNewConstructorMethodChain)
{
ExecuteTest(
"public class TestClass {\n"
" int a = 123;\n"
" int b = 246;\n"
" TestClass testSetA(int x) { a = x; return this; }\n"
" TestClass testSetB(int y) { b = y; return this; }\n"
"}\n"
"extern void NewConstructorMethodChain() {\n"
" TestClass tc;\n"
" tc = new TestClass().testSetA(111).testSetB(222);\n"
" ASSERT(tc.a == 111);\n"
" ASSERT(tc.b == 222);\n"
"}\n"
);
}