CBotCall refactoring

dev-time-step
krzys-h 2015-12-24 00:41:33 +01:00
parent 00221c9a3f
commit 3170395576
4 changed files with 129 additions and 278 deletions

View File

@ -17,7 +17,6 @@
* along with this program. If not, see http://gnu.org/licenses * along with this program. If not, see http://gnu.org/licenses
*/ */
// Modules inlcude
#include "CBot/CBotCall.h" #include "CBot/CBotCall.h"
#include "CBot/CBotToken.h" #include "CBot/CBotToken.h"
@ -28,279 +27,153 @@
#include "CBot/CBotVar/CBotVar.h" #include "CBot/CBotVar/CBotVar.h"
// Local include std::map<std::string, std::unique_ptr<CBotCall>> CBotCall::m_list = std::map<std::string, std::unique_ptr<CBotCall>>();
void* CBotCall::m_user = nullptr;
// Global include CBotCall::CBotCall(const std::string& name, RuntimeFunc rExec, CompileFunc rCompile)
////////////////////////////////////////////////////////////////////////////////
CBotCall* CBotCall::m_ListCalls = nullptr;
void* CBotCall::m_pUser = nullptr;
////////////////////////////////////////////////////////////////////////////////
CBotCall::CBotCall(const std::string& name,
bool rExec(CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser),
CBotTypResult rCompile(CBotVar*& pVar, void* pUser))
{ {
m_name = name; m_name = name;
m_rExec = rExec; m_rExec = rExec;
m_rComp = rCompile; m_rComp = rCompile;
m_nFuncIdent = CBotVar::NextUniqNum(); m_ident = CBotVar::NextUniqNum();
} }
////////////////////////////////////////////////////////////////////////////////
CBotCall::~CBotCall() CBotCall::~CBotCall()
{ {
} }
//////////////////////////////////////////////////////////////////////////////// void CBotCall::Clear()
void CBotCall::Free()
{ {
delete CBotCall::m_ListCalls; m_list.clear();
m_ListCalls = nullptr;
} }
//////////////////////////////////////////////////////////////////////////////// bool CBotCall::AddFunction(const std::string& name, RuntimeFunc rExec, CompileFunc rCompile)
bool CBotCall::AddFunction(const std::string& name,
bool rExec(CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser),
CBotTypResult rCompile(CBotVar*& pVar, void* pUser))
{ {
CBotCall* p = m_ListCalls; m_list[name] = std::unique_ptr<CBotCall>(new CBotCall(name, rExec, rCompile));
CBotCall* pp = nullptr;
if ( p != nullptr ) while ( p->m_next != nullptr )
{
if ( p->GetName() == name )
{
// frees redefined function
if ( pp ) pp->m_next = p->m_next;
else m_ListCalls = p->m_next;
pp = p;
p = p->m_next;
pp->m_next = nullptr; // not to destroy the following list
delete pp;
continue;
}
pp = p; // previous pointer
p = p->m_next;
}
pp = new CBotCall(name, rExec, rCompile);
if (p) p->m_next = pp;
else m_ListCalls = pp;
return true; return true;
} }
////////////////////////////////////////////////////////////////////////////////
CBotTypResult CBotCall::CompileCall(CBotToken* &p, CBotVar** ppVar, CBotCStack* pStack, long& nIdent) CBotTypResult CBotCall::CompileCall(CBotToken* &p, CBotVar** ppVar, CBotCStack* pStack, long& nIdent)
{ {
nIdent = 0; nIdent = 0;
CBotCall* pt = m_ListCalls; if (m_list.count(p->GetString()) == 0)
std::string name = p->GetString(); return -1;
while ( pt != nullptr ) CBotCall* pt = m_list[p->GetString()].get();
nIdent = pt->m_ident;
std::unique_ptr<CBotVar> args = std::unique_ptr<CBotVar>(MakeListVars(ppVar));
CBotVar* var = args.get(); // TODO: This shouldn't be a reference
CBotTypResult r = pt->m_rComp(var, m_user);
// if a class is returned, it is actually a pointer
if (r.GetType() == CBotTypClass) r.SetType(CBotTypPointer);
if (r.GetType() > 20) // error?
{ {
if ( pt->m_name == name ) pStack->SetError(static_cast<CBotError>(r.GetType()), p);
{
CBotVar* pVar = MakeListVars(ppVar);
CBotVar* pVar2 = pVar;
CBotTypResult r = pt->m_rComp(pVar2, m_pUser);
int ret = r.GetType();
// if a class is returned, it is actually a pointer
if ( ret == CBotTypClass ) r.SetType( ret = CBotTypPointer );
if ( ret > 20 )
{
if (pVar2) pStack->SetError(static_cast<CBotError>(ret), p /*pVar2->GetToken()*/ );
}
delete pVar;
nIdent = pt->m_nFuncIdent;
return r;
}
pt = pt->m_next;
} }
return -1;
return r;
} }
//////////////////////////////////////////////////////////////////////////////// void CBotCall::SetUserPtr(void* pUser)
void CBotCall::SetPUser(void* pUser)
{ {
m_pUser = pUser; m_user = pUser;
} }
////////////////////////////////////////////////////////////////////////////////
bool CBotCall::CheckCall(const std::string& name) bool CBotCall::CheckCall(const std::string& name)
{ {
CBotCall* p = m_ListCalls; return m_list.count(name) > 0;
while ( p != nullptr )
{
if ( name == p->GetName() ) return true;
p = p->m_next;
}
return false;
} }
////////////////////////////////////////////////////////////////////////////////
std::string CBotCall::GetName()
{
return m_name;
}
////////////////////////////////////////////////////////////////////////////////
int CBotCall::DoCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack, CBotTypResult& rettype) int CBotCall::DoCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack, CBotTypResult& rettype)
{ {
CBotCall* pt = m_ListCalls; CBotCall* pt = nullptr;
if ( nIdent ) while ( pt != nullptr ) if (nIdent > 0)
{ {
if ( pt->m_nFuncIdent == nIdent ) for (const auto& it : m_list)
{ {
goto fund; if (it.second->m_ident == nIdent)
}
pt = pt->m_next;
}
pt = m_ListCalls;
if ( token != nullptr )
{
std::string name = token->GetString();
while ( pt != nullptr )
{
if ( pt->m_name == name )
{ {
nIdent = pt->m_nFuncIdent; pt = it.second.get();
goto fund;
} }
pt = pt->m_next;
} }
} }
return -1; if (pt == nullptr)
fund:
#if !STACKRUN
// lists the parameters depending on the contents of the stack (pStackVar)
CBotVar* pVar = MakeListVars(ppVar, true);
CBotVar* pVarToDelete = pVar;
// creates a variable to the result
CBotVar* pResult = rettype.Eq(0) ? nullptr : CBotVar::Create("", rettype);
CBotVar* pRes = pResult;
int Exception = 0;
int res = pt->m_rExec(pVar, pResult, Exception, pStack->GetPUser());
if ( pResult != pRes ) delete pRes; // different result if made
delete pVarToDelete;
if (res == false)
{ {
if (Exception!=0) if (token != nullptr)
{ {
pStack->SetError(Exception, token); if (m_list.count(token->GetString()) > 0)
{
pt = m_list[token->GetString()].get();
nIdent = pt->m_ident;
}
} }
delete pResult;
return false;
} }
pStack->SetVar(pResult);
if ( rettype.GetType() > 0 && pResult == nullptr ) if (pt == nullptr)
{ return -1;
pStack->SetError(CBotErrNoRetVal, token);
}
nIdent = pt->m_nFuncIdent;
return true;
#else
CBotStack* pile = pStack->AddStackEOX(pt); CBotStack* pile = pStack->AddStackEOX(pt);
if ( pile == EOX ) return true; if (pile == EOX) return true;
// lists the parameters depending on the contents of the stack (pStackVar) // lists the parameters depending on the contents of the stack (pStackVar)
CBotVar* pVar = MakeListVars(ppVar, true); CBotVar* pVar = MakeListVars(ppVar, true);
// CBotVar* pVarToDelete = pVar;
// creates a variable to the result // creates a variable to the result
CBotVar* pResult = rettype.Eq(0) ? nullptr : CBotVar::Create("", rettype); CBotVar* pResult = rettype.Eq(CBotTypVoid) ? nullptr : CBotVar::Create("", rettype);
pile->SetVar( pVar ); pile->SetVar(pVar);
CBotStack* pile2 = pile->AddStack(); CBotStack* pile2 = pile->AddStack();
pile2->SetVar( pResult ); pile2->SetVar(pResult);
pile->SetError(CBotNoErr, token); // for the position on error + away pile->SetError(CBotNoErr, token); // save token for the position in case of error
return pt->Run( pStack ); return pt->Run(pStack);
#endif
} }
#if STACKRUN
////////////////////////////////////////////////////////////////////////////////
bool CBotCall::RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack) bool CBotCall::RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack)
{ {
CBotCall* pt = m_ListCalls; if (m_list.count(token->GetString()) == 0)
return false;
{ CBotCall* pt = m_list[token->GetString()].get();
std::string name = token->GetString(); nIdent = pt->m_ident;
while ( pt != nullptr )
{
if ( pt->m_name == name )
{
nIdent = pt->m_nFuncIdent;
CBotStack* pile = pStack->RestoreStackEOX(pt); CBotStack* pile = pStack->RestoreStackEOX(pt);
if ( pile == nullptr ) return true; if ( pile == nullptr ) return true;
// CBotStack* pile2 = pile->RestoreStack(); pile->RestoreStack();
pile->RestoreStack(); return true;
return true;
}
pt = pt->m_next;
}
}
return false;
} }
////////////////////////////////////////////////////////////////////////////////
bool CBotCall::Run(CBotStack* pStack) bool CBotCall::Run(CBotStack* pStack)
{ {
CBotStack* pile = pStack->AddStackEOX(this); CBotStack* pile = pStack->AddStackEOX(this);
if ( pile == EOX ) return true; if ( pile == EOX ) return true;
CBotVar* pVar = pile->GetVar(); CBotVar* args = pile->GetVar();
CBotStack* pile2 = pile->AddStack(); CBotStack* pile2 = pile->AddStack();
CBotVar* pResult = pile2->GetVar();
CBotVar* pRes = pResult;
int Exception = 0; // TODO: change this to CBotError CBotVar* result = pile2->GetVar();
int res = m_rExec(pVar, pResult, Exception, pStack->GetPUser());
if (res == false) int exception = CBotNoErr; // TODO: Change to CBotError
bool res = m_rExec(args, result, exception, pStack->GetPUser());
if (!res)
{ {
if (Exception!=0) if (exception != CBotNoErr)
{ {
pStack->SetError(static_cast<CBotError>(Exception)); pStack->SetError(static_cast<CBotError>(exception));
} }
if ( pResult != pRes ) delete pResult; // different result if made
return false; return false;
} }
if ( pResult != nullptr ) pStack->SetCopyVar( pResult ); if (result != nullptr) pStack->SetCopyVar(result);
if ( pResult != pRes ) delete pResult; // different result if made
return true; return true;
} }
#endif

View File

@ -20,8 +20,12 @@
#pragma once #pragma once
#include "CBot/CBotUtils.h" #include "CBot/CBotUtils.h"
#include "CBot/CBotEnums.h"
#include "CBot/CBotDefines.h"
#include <string> #include <string>
#include <map>
#include <memory>
class CBotStack; class CBotStack;
class CBotCStack; class CBotCStack;
@ -29,114 +33,92 @@ class CBotVar;
class CBotTypResult; class CBotTypResult;
class CBotToken; class CBotToken;
#define STACKRUN 1 //! \def return execution directly on a suspended routine /**
* \brief Class used for external calls
/*! *
* \brief The CBotCall class. Class for routine calls (external). * \see CBotProgram::AddFunction() for information on how to add your functions to this list
*/ */
class CBotCall : public CBotLinkedList<CBotCall> class CBotCall : public CBotLinkedList<CBotCall>
{ {
public: public:
typedef bool (*RuntimeFunc)(CBotVar* args, CBotVar* result, int& exception, void* user);
typedef CBotTypResult (*CompileFunc)(CBotVar*& args, void* user);
/*! /**
* \brief CBotCall * \brief Constructor
* \param name * \param name Function name
* \param rExec * \param rExec Runtime function
* \param rCompile * \param rCompile Compilation function
* \see CBotProgram::AddFunction()
*/ */
CBotCall(const std::string& name, CBotCall(const std::string& name, RuntimeFunc rExec, CompileFunc rCompile);
bool rExec(CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser),
CBotTypResult rCompile(CBotVar*& pVar, void* pUser));
/*! /**
* \brief ~CBotCall * \brief Destructor
*/ */
~CBotCall(); ~CBotCall();
/*! /**
* \brief AddFunction * \brief Add a new function to the list
* \param name * \param name Function name
* \param rExec * \param rExec Runtime function
* \param rCompile * \param rCompile Compilation function
* \return * \return true
*/ */
static bool AddFunction(const std::string& name, static bool AddFunction(const std::string& name, RuntimeFunc rExec, CompileFunc rCompile);
bool rExec(CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser),
CBotTypResult rCompile(CBotVar*& pVar, void* pUser));
/*! /**
* \brief CompileCall Is acceptable by a call procedure name and given * \brief Find and call compile function
* parameters. *
* \param p * \todo Document
* \param ppVars
* \param pStack
* \param nIdent
* \return
*/ */
static CBotTypResult CompileCall(CBotToken* &p, CBotVar** ppVars, CBotCStack* pStack, long& nIdent); static CBotTypResult CompileCall(CBotToken* &p, CBotVar** ppVars, CBotCStack* pStack, long& nIdent);
/*! /**
* \brief CheckCall * \brief Check if function with given name has been defined
* \param name * \param name Name to check
* \return * \return true if function was defined
*/ */
static bool CheckCall(const std::string& name); static bool CheckCall(const std::string& name);
/*! /**
* \brief DoCall * \brief Find and call runtime function
* \param nIdent *
* \param token * \todo Document
* \param ppVars
* \param pStack
* \param rettype
* \return
*/ */
static int DoCall(long& nIdent, CBotToken* token, CBotVar** ppVars, CBotStack* pStack, CBotTypResult& rettype); static int DoCall(long& nIdent, CBotToken* token, CBotVar** ppVars, CBotStack* pStack, CBotTypResult& rettype);
#if STACKRUN /**
* \brief Execute the runtime function
/*! * \param pStack Stack to execute the function on
* \brief Run * \return false if function requested interruption, true otherwise
* \param pStack
* \return
*/ */
bool Run(CBotStack* pStack); bool Run(CBotStack* pStack);
/*! /**
* \brief RestoreCall * \brief Restore execution status after loading saved state
* \param nIdent *
* \param token * \todo Document
* \param ppVar
* \param pStack
* \return
*/ */
static bool RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack); static bool RestoreCall(long& nIdent, CBotToken* token, CBotVar** ppVar, CBotStack* pStack);
#endif
/*! /**
* \brief GetName * \brief Set user pointer to pass to compile/runtime functions
* \return * \param pUser User pointer
*/ */
std::string GetName(); static void SetUserPtr(void* pUser);
/*! /**
* \brief SetPUser * \brief Reset the list of registered functions
* \param pUser
*/ */
static void SetPUser(void* pUser); static void Clear();
/*!
* \brief Free
*/
static void Free();
private: private:
static CBotCall* m_ListCalls; static std::map<std::string, std::unique_ptr<CBotCall>> m_list;
static void* m_pUser; static void* m_user;
long m_nFuncIdent;
long m_ident;
std::string m_name; std::string m_name;
bool (*m_rExec) (CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser); RuntimeFunc m_rExec;
CBotTypResult (*m_rComp) (CBotVar* &pVar, void* pUser); CompileFunc m_rComp;
}; };

View File

@ -186,7 +186,7 @@ enum TokenType {
* *
* Also note that these can't overlap with CBotType, see CBotTypResult for explanation * Also note that these can't overlap with CBotType, see CBotTypResult for explanation
*/ */
enum CBotError enum CBotError : int
{ {
CBotNoErr = 0, CBotNoErr = 0,

View File

@ -77,7 +77,7 @@ bool CBotProgram::Compile(const std::string& program, std::vector<std::string>&
CBotToken* p = tokens.get()->GetNext(); // skips the first token (separator) CBotToken* p = tokens.get()->GetNext(); // skips the first token (separator)
pStack->SetProgram(this); // defined used routines pStack->SetProgram(this); // defined used routines
CBotCall::SetPUser(pUser); CBotCall::SetUserPtr(pUser);
// Step 2. Find all function and class definitions // Step 2. Find all function and class definitions
while ( pStack->IsOk() && p != nullptr && p->GetType() != 0) while ( pStack->IsOk() && p != nullptr && p->GetType() != 0)
@ -209,7 +209,6 @@ bool CBotProgram::Run(void* pUser, int timer)
m_pStack->SetBotCall(this); // bases for routines m_pStack->SetBotCall(this); // bases for routines
#if STACKRUN
// resumes execution on the top of the stack // resumes execution on the top of the stack
ok = m_pStack->Execute(); ok = m_pStack->Execute();
if ( ok ) if ( ok )
@ -217,9 +216,6 @@ bool CBotProgram::Run(void* pUser, int timer)
// returns to normal execution // returns to normal execution
ok = m_entryPoint->Execute(nullptr, m_pStack, m_thisVar); ok = m_entryPoint->Execute(nullptr, m_pStack, m_thisVar);
} }
#else
ok = m_pRun->Execute(nullptr, m_pStack, m_thisVar);
#endif
// completed on a mistake? // completed on a mistake?
if (!ok && !m_pStack->IsOk()) if (!ok && !m_pStack->IsOk())
@ -440,6 +436,6 @@ void CBotProgram::Init()
void CBotProgram::Free() void CBotProgram::Free()
{ {
CBotToken::ClearDefineNum() ; CBotToken::ClearDefineNum() ;
CBotCall ::Free() ; CBotCall::Clear() ;
CBotClass::Free() ; CBotClass::Free() ;
} }