Add method chaining for class constructor calls
parent
d411c5ebc0
commit
3146d4ef35
|
@ -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();
|
||||
|
|
|
@ -85,6 +85,9 @@ private:
|
|||
//! Constructor method unique identifier
|
||||
long m_nMethodeIdent;
|
||||
|
||||
//! Instruction to chain method calls after constructor
|
||||
CBotInstr* m_exprRetVar;
|
||||
|
||||
};
|
||||
|
||||
} // namespace CBot
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -66,6 +66,9 @@ private:
|
|||
long m_nMethodeIdent;
|
||||
CBotToken m_vartoken;
|
||||
|
||||
//! Instruction to chain method calls after constructor
|
||||
CBotInstr* m_exprRetVar;
|
||||
|
||||
};
|
||||
|
||||
} // namespace CBot
|
||||
|
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue