939 lines
31 KiB
C++
939 lines
31 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "CBot/CBotInstr/CBotFunction.h"
|
|
|
|
#include "CBot/CBotInstr/CBotInstrUtils.h"
|
|
|
|
#include "CBot/CBotInstr/CBotBlock.h"
|
|
#include "CBot/CBotInstr/CBotTwoOpExpr.h"
|
|
#include "CBot/CBotInstr/CBotExpression.h"
|
|
#include "CBot/CBotInstr/CBotEmpty.h"
|
|
#include "CBot/CBotInstr/CBotListArray.h"
|
|
|
|
#include "CBot/CBotStack.h"
|
|
#include "CBot/CBotCStack.h"
|
|
#include "CBot/CBotClass.h"
|
|
#include "CBot/CBotDefParam.h"
|
|
#include "CBot/CBotUtils.h"
|
|
|
|
#include "CBot/CBotVar/CBotVar.h"
|
|
|
|
#include <cassert>
|
|
#include <sstream>
|
|
|
|
namespace CBot
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction::CBotFunction()
|
|
{
|
|
m_param = nullptr; // empty parameter list
|
|
m_block = nullptr; // the instruction block
|
|
m_next = nullptr; // functions can be chained
|
|
m_bPublic = false; // function not public
|
|
m_bExtern = false; // function not extern
|
|
m_pProg = nullptr;
|
|
// m_nThisIdent = 0;
|
|
m_nFuncIdent = 0;
|
|
m_bSynchro = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::set<CBotFunction*> CBotFunction::m_publicFunctions{};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction::~CBotFunction()
|
|
{
|
|
delete m_param; // empty parameter list
|
|
delete m_block; // the instruction block
|
|
delete m_next;
|
|
|
|
// remove public list if there is
|
|
if (m_bPublic)
|
|
{
|
|
m_publicFunctions.erase(this);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::IsPublic()
|
|
{
|
|
return m_bPublic;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::IsExtern()
|
|
{
|
|
return m_bExtern;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::GetPosition(int& start, int& stop, CBotGet modestart, CBotGet modestop)
|
|
{
|
|
start = m_extern.GetStart();
|
|
stop = m_closeblk.GetEnd();
|
|
|
|
if (modestart == GetPosExtern)
|
|
{
|
|
start = m_extern.GetStart();
|
|
}
|
|
if (modestop == GetPosExtern)
|
|
{
|
|
stop = m_extern.GetEnd();
|
|
}
|
|
if (modestart == GetPosNom)
|
|
{
|
|
start = m_token.GetStart();
|
|
}
|
|
if (modestop == GetPosNom)
|
|
{
|
|
stop = m_token.GetEnd();
|
|
}
|
|
if (modestart == GetPosParam)
|
|
{
|
|
start = m_openpar.GetStart();
|
|
}
|
|
if (modestop == GetPosParam)
|
|
{
|
|
stop = m_closepar.GetEnd();
|
|
}
|
|
if (modestart == GetPosBloc)
|
|
{
|
|
start = m_openblk.GetStart();
|
|
}
|
|
if (modestop == GetPosBloc)
|
|
{
|
|
stop = m_closeblk.GetEnd();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunction* finput, bool bLocal)
|
|
{
|
|
CBotToken* pp;
|
|
CBotFunction* func = finput;
|
|
if ( func == nullptr ) func = new CBotFunction();
|
|
|
|
CBotCStack* pStk = pStack->TokenStack(p, bLocal);
|
|
|
|
// func->m_nFuncIdent = CBotVar::NextUniqNum();
|
|
|
|
while (true)
|
|
{
|
|
if ( IsOfType(p, ID_PUBLIC) )
|
|
{
|
|
func->m_bPublic = true;
|
|
continue;
|
|
}
|
|
pp = p;
|
|
if ( IsOfType(p, ID_EXTERN) )
|
|
{
|
|
func->m_extern = *pp; // for the position of the word "extern"
|
|
func->m_bExtern = true;
|
|
// func->m_bPublic = true; // therefore also public!
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
func->m_retToken = *p;
|
|
// CBotClass* pClass;
|
|
func->m_retTyp = TypeParam(p, pStk); // type of the result
|
|
|
|
if (func->m_retTyp.GetType() >= 0)
|
|
{
|
|
CBotToken* pp = p;
|
|
func->m_token = *p;
|
|
|
|
if ( IsOfType(p, ID_NOT) )
|
|
{
|
|
CBotToken d(std::string("~") + p->GetString());
|
|
func->m_token = d;
|
|
}
|
|
|
|
// un nom de fonction est-il là ?
|
|
if (IsOfType(p, TokenTypVar))
|
|
{
|
|
if ( IsOfType( p, ID_DBLDOTS ) ) // method for a class
|
|
{
|
|
func->m_MasterClass = pp->GetString();
|
|
func->m_classToken = *pp;
|
|
CBotClass* pClass = CBotClass::Find(pp);
|
|
if ( pClass == nullptr ) goto bad;
|
|
|
|
// pp = p;
|
|
func->m_token = *p;
|
|
if (!IsOfType(p, TokenTypVar)) goto bad;
|
|
|
|
}
|
|
func->m_openpar = *p;
|
|
func->m_param = CBotDefParam::Compile(p, pStk );
|
|
func->m_closepar = *(p->GetPrev());
|
|
if (pStk->IsOk())
|
|
{
|
|
pStk->SetRetType(func->m_retTyp); // for knowledge what type returns
|
|
|
|
if (!func->m_MasterClass.empty())
|
|
{
|
|
// return "this" known
|
|
CBotVar* pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, func->m_MasterClass ));
|
|
pThis->SetInit(CBotVar::InitType::IS_POINTER);
|
|
// pThis->SetUniqNum(func->m_nThisIdent = -2); //CBotVar::NextUniqNum() will not
|
|
pThis->SetUniqNum(-2);
|
|
pStk->AddVar(pThis);
|
|
|
|
// initialize variables acording to This
|
|
// only saves the pointer to the first,
|
|
// the rest is chained
|
|
CBotVar* pv = pThis->GetItemList();
|
|
// int num = 1;
|
|
while (pv != nullptr)
|
|
{
|
|
CBotVar* pcopy = CBotVar::Create(pv);
|
|
// pcopy->SetInit(2);
|
|
pcopy->Copy(pv);
|
|
pcopy->SetPrivate(pv->GetPrivate());
|
|
// pcopy->SetUniqNum(pv->GetUniqNum()); //num++);
|
|
pStk->AddVar(pcopy);
|
|
pv = pv->GetNext();
|
|
}
|
|
}
|
|
|
|
// and compiles the following instruction block
|
|
func->m_openblk = *p;
|
|
func->m_block = CBotBlock::Compile(p, pStk, false);
|
|
func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken();
|
|
if ( pStk->IsOk() )
|
|
{
|
|
return pStack->ReturnFunc(func, pStk);
|
|
}
|
|
}
|
|
}
|
|
bad:
|
|
pStk->SetError(CBotErrNoFunc, p);
|
|
}
|
|
pStk->SetError(CBotErrNoType, p);
|
|
if ( finput == nullptr ) delete func;
|
|
return pStack->ReturnFunc(nullptr, pStk);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction* CBotFunction::Compile1(CBotToken* &p, CBotCStack* pStack, CBotClass* pClass)
|
|
{
|
|
CBotFunction* func = new CBotFunction();
|
|
func->m_nFuncIdent = CBotVar::NextUniqNum();
|
|
|
|
CBotCStack* pStk = pStack->TokenStack(p, true);
|
|
|
|
while (true)
|
|
{
|
|
if ( IsOfType(p, ID_PUBLIC) )
|
|
{
|
|
// func->m_bPublic = true; // will be done in two passes
|
|
continue;
|
|
}
|
|
if ( IsOfType(p, ID_EXTERN) )
|
|
{
|
|
func->m_bExtern = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
func->m_retToken = *p;
|
|
func->m_retTyp = TypeParam(p, pStack); // type of the result
|
|
|
|
if (func->m_retTyp.GetType() >= 0)
|
|
{
|
|
CBotToken* pp = p;
|
|
func->m_token = *p;
|
|
|
|
if ( IsOfType(p, ID_NOT) )
|
|
{
|
|
CBotToken d(std::string("~") + p->GetString());
|
|
func->m_token = d;
|
|
}
|
|
|
|
// un nom de fonction est-il là ?
|
|
if (IsOfType(p, TokenTypVar))
|
|
{
|
|
if ( IsOfType( p, ID_DBLDOTS ) ) // method for a class
|
|
{
|
|
func->m_MasterClass = pp->GetString();
|
|
CBotClass* pClass = CBotClass::Find(pp);
|
|
if ( pClass == nullptr )
|
|
{
|
|
pStk->SetError(CBotErrNotClass, pp);
|
|
goto bad;
|
|
}
|
|
|
|
pp = p;
|
|
func->m_token = *p;
|
|
if (!IsOfType(p, TokenTypVar)) goto bad;
|
|
|
|
}
|
|
func->m_param = CBotDefParam::Compile(p, pStk );
|
|
if (pStk->IsOk())
|
|
{
|
|
// looks if the function exists elsewhere
|
|
if (( pClass != nullptr || !pStack->CheckCall(pp, func->m_param)) &&
|
|
( pClass == nullptr || !pClass->CheckCall(pStack->GetProgram(), func->m_param, pp)) )
|
|
{
|
|
if (IsOfType(p, ID_OPBLK))
|
|
{
|
|
int level = 1;
|
|
// and skips the following instruction block
|
|
do
|
|
{
|
|
int type = p->GetType();
|
|
p = p->GetNext();
|
|
if (type == ID_OPBLK) level++;
|
|
if (type == ID_CLBLK) level--;
|
|
}
|
|
while (level > 0 && p != nullptr);
|
|
|
|
return pStack->ReturnFunc(func, pStk);
|
|
}
|
|
pStk->SetError(CBotErrOpenBlock, p);
|
|
}
|
|
}
|
|
pStk->SetError(CBotErrRedefFunc, pp);
|
|
}
|
|
bad:
|
|
pStk->SetError(CBotErrNoFunc, p);
|
|
}
|
|
pStk->SetError(CBotErrNoType, p);
|
|
delete func;
|
|
return pStack->ReturnFunc(nullptr, pStk);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::Execute(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance)
|
|
{
|
|
CBotStack* pile = pj->AddStack(this, CBotStack::BlockVisibilityType::FUNCTION); // one end of stack local to this function
|
|
// if ( pile == EOX ) return true;
|
|
|
|
pile->SetProgram(m_pProg); // bases for routines
|
|
|
|
if ( pile->GetState() == 0 )
|
|
{
|
|
if ( !m_param->Execute(ppVars, pile) ) return false; // define parameters
|
|
pile->IncState();
|
|
}
|
|
|
|
if ( pile->GetState() == 1 && !m_MasterClass.empty() )
|
|
{
|
|
// makes "this" known
|
|
CBotVar* pThis = nullptr;
|
|
if ( pInstance == nullptr )
|
|
{
|
|
pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, m_MasterClass ));
|
|
}
|
|
else
|
|
{
|
|
if (m_MasterClass != pInstance->GetClass()->GetName())
|
|
{
|
|
pile->SetError(CBotErrBadType2, &m_classToken);
|
|
return false;
|
|
}
|
|
|
|
pThis = CBotVar::Create("this", CBotTypResult( CBotTypPointer, m_MasterClass ));
|
|
pThis->SetPointer(pInstance);
|
|
}
|
|
assert(pThis != nullptr);
|
|
pThis->SetInit(CBotVar::InitType::IS_POINTER);
|
|
|
|
// pThis->SetUniqNum(m_nThisIdent);
|
|
pThis->SetUniqNum(-2);
|
|
pile->AddVar(pThis);
|
|
|
|
pile->IncState();
|
|
}
|
|
|
|
if ( pile->IfStep() ) return false;
|
|
|
|
if ( !m_block->Execute(pile) )
|
|
{
|
|
if ( pile->GetError() < 0 )
|
|
pile->SetError( CBotNoErr );
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return pj->Return(pile);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotFunction::RestoreState(CBotVar** ppVars, CBotStack* &pj, CBotVar* pInstance)
|
|
{
|
|
CBotStack* pile = pj->RestoreStack(this); // one end of stack local to this function
|
|
if ( pile == nullptr ) return;
|
|
CBotStack* pile2 = pile;
|
|
|
|
pile->SetProgram(m_pProg); // bases for routines
|
|
|
|
if ( pile->GetBlock() != CBotStack::BlockVisibilityType::FUNCTION)
|
|
{
|
|
CBotStack* pile2 = pile->RestoreStack(nullptr); // one end of stack local to this function
|
|
if ( pile2 == nullptr ) return;
|
|
pile->SetState(pile->GetState() + pile2->GetState());
|
|
pile2->Delete();
|
|
}
|
|
|
|
m_param->RestoreState(pile2, true); // parameters
|
|
|
|
if ( !m_MasterClass.empty() )
|
|
{
|
|
CBotVar* pThis = pile->FindVar("this");
|
|
pThis->SetInit(CBotVar::InitType::IS_POINTER);
|
|
pThis->SetUniqNum(-2);
|
|
}
|
|
|
|
m_block->RestoreState(pile2, true);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotFunction::AddNext(CBotFunction* p)
|
|
{
|
|
CBotFunction* pp = this;
|
|
while (pp->m_next != nullptr) pp = pp->m_next;
|
|
|
|
pp->m_next = p;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotTypResult CBotFunction::CompileCall(CBotFunction* localFunctionList, const std::string &name, CBotVar** ppVars, long &nIdent)
|
|
{
|
|
CBotTypResult type;
|
|
if (!FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type))
|
|
{
|
|
// Reset the identifier to "not found" value
|
|
nIdent = 0;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction* CBotFunction::FindLocalOrPublic(CBotFunction* localFunctionList, long &nIdent, const std::string &name,
|
|
CBotVar** ppVars, CBotTypResult &TypeOrError, bool bPublic)
|
|
{
|
|
TypeOrError.SetType(CBotErrUndefCall); // no routine of the name
|
|
CBotFunction* pt;
|
|
|
|
if ( nIdent )
|
|
{
|
|
if ( localFunctionList != nullptr ) for ( pt = localFunctionList ; pt != nullptr ; pt = pt->m_next )
|
|
{
|
|
if ( pt->m_nFuncIdent == nIdent )
|
|
{
|
|
TypeOrError = pt->m_retTyp;
|
|
return pt;
|
|
}
|
|
}
|
|
|
|
// search the list of public functions
|
|
for (CBotFunction* pt : m_publicFunctions)
|
|
{
|
|
if (pt->m_nFuncIdent == nIdent)
|
|
{
|
|
TypeOrError = pt->m_retTyp;
|
|
return pt;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( name.empty() ) return nullptr;
|
|
|
|
std::map<CBotFunction*, int> funcMap;
|
|
|
|
if ( localFunctionList != nullptr )
|
|
{
|
|
for ( pt = localFunctionList ; pt != nullptr ; pt = pt->m_next )
|
|
{
|
|
if ( pt->m_token.GetString() == name )
|
|
{
|
|
int i = 0;
|
|
int alpha = 0; // signature of parameters
|
|
// parameters are compatible?
|
|
CBotDefParam* pv = pt->m_param; // expected list of parameters
|
|
CBotVar* pw = ppVars[i++]; // provided list parameter
|
|
while ( pv != nullptr && pw != nullptr)
|
|
{
|
|
CBotTypResult paramType = pv->GetTypResult();
|
|
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
|
|
if (!TypesCompatibles(paramType, argType))
|
|
{
|
|
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
|
|
break;
|
|
}
|
|
|
|
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
|
|
{
|
|
CBotClass* c1 = paramType.GetClass();
|
|
CBotClass* c2 = argType.GetClass();
|
|
while (c2 != c1 && c2 != nullptr) // implicit cast
|
|
{
|
|
alpha += 10;
|
|
c2 = c2->GetParent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
|
}
|
|
pv = pv->GetNext();
|
|
pw = ppVars[i++];
|
|
}
|
|
if ( pw != nullptr )
|
|
{
|
|
if ( !funcMap.empty() ) continue;
|
|
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
|
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
|
continue; // too many parameters
|
|
}
|
|
if ( pv != nullptr )
|
|
{
|
|
if ( !funcMap.empty() ) continue;
|
|
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
|
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
|
continue; // not enough parameters
|
|
}
|
|
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bPublic )
|
|
{
|
|
for (CBotFunction* pt : m_publicFunctions)
|
|
{
|
|
if ( pt->m_token.GetString() == name )
|
|
{
|
|
int i = 0;
|
|
int alpha = 0; // signature of parameters
|
|
// 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)
|
|
{
|
|
CBotTypResult paramType = pv->GetTypResult();
|
|
CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
|
|
if (!TypesCompatibles(paramType, argType))
|
|
{
|
|
if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam);
|
|
break;
|
|
}
|
|
|
|
if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer))
|
|
{
|
|
CBotClass* c1 = paramType.GetClass();
|
|
CBotClass* c2 = argType.GetClass();
|
|
while (c2 != c1 && c2 != nullptr) // implicit cast
|
|
{
|
|
alpha += 10;
|
|
c2 = c2->GetParent();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC);
|
|
alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive!
|
|
}
|
|
pv = pv->GetNext();
|
|
pw = ppVars[i++];
|
|
}
|
|
if ( pw != nullptr )
|
|
{
|
|
if ( !funcMap.empty() ) continue; // previous useable function
|
|
if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam);
|
|
if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam);
|
|
continue; // to many parameters
|
|
}
|
|
if ( pv != nullptr )
|
|
{
|
|
if ( !funcMap.empty() ) continue; // previous useable function
|
|
if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam);
|
|
if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam);
|
|
continue; // not enough parameters
|
|
}
|
|
funcMap.insert( std::pair<CBotFunction*, int>(pt, alpha) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !funcMap.empty() )
|
|
{
|
|
auto it = funcMap.begin();
|
|
CBotFunction* pFunc = it->first; // the best function found
|
|
signed int delta = it->second; // seeks the lowest signature
|
|
|
|
for (++it ; it != funcMap.end() ; it++)
|
|
{
|
|
if (it->second < delta) // a better signature?
|
|
{
|
|
TypeOrError.SetType(CBotNoErr);
|
|
pFunc = it->first;
|
|
delta = it->second;
|
|
continue;
|
|
}
|
|
|
|
if (it->second == delta) TypeOrError.SetType(CBotErrAmbiguousCall);
|
|
}
|
|
|
|
if (TypeOrError.Eq(CBotErrAmbiguousCall)) return nullptr;
|
|
nIdent = pFunc->m_nFuncIdent;
|
|
TypeOrError = pFunc->m_retTyp;
|
|
return pFunc;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int CBotFunction::DoCall(CBotProgram* program, CBotFunction* localFunctionList, long &nIdent, const std::string &name,
|
|
CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken)
|
|
{
|
|
CBotTypResult type;
|
|
CBotFunction* pt = nullptr;
|
|
|
|
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
|
|
|
|
if ( pt != nullptr )
|
|
{
|
|
CBotStack* pStk1 = pStack->AddStack(pt, CBotStack::BlockVisibilityType::FUNCTION); // to put "this"
|
|
// if ( pStk1 == EOX ) return true;
|
|
|
|
pStk1->SetProgram(pt->m_pProg); // it may have changed module
|
|
|
|
if ( pStk1->IfStep() ) return false;
|
|
|
|
CBotStack* pStk3 = pStk1->AddStack(nullptr, CBotStack::BlockVisibilityType::BLOCK); // parameters
|
|
|
|
// preparing parameters on the stack
|
|
|
|
if ( pStk1->GetState() == 0 )
|
|
{
|
|
if ( !pt->m_MasterClass.empty() )
|
|
{
|
|
CBotVar* pInstance = program->m_thisVar;
|
|
// make "this" known
|
|
CBotVar* pThis ;
|
|
if ( pInstance == nullptr )
|
|
{
|
|
pThis = CBotVar::Create("this", CBotTypResult( CBotTypClass, pt->m_MasterClass ));
|
|
}
|
|
else
|
|
{
|
|
if (pt->m_MasterClass != pInstance->GetClass()->GetName())
|
|
{
|
|
pStack->SetError(CBotErrBadType2, &pt->m_classToken);
|
|
return false;
|
|
}
|
|
|
|
pThis = CBotVar::Create("this", CBotTypResult( CBotTypPointer, pt->m_MasterClass ));
|
|
pThis->SetPointer(pInstance);
|
|
}
|
|
assert(pThis != nullptr);
|
|
pThis->SetInit(CBotVar::InitType::IS_POINTER);
|
|
|
|
pThis->SetUniqNum(-2);
|
|
pStk1->AddVar(pThis);
|
|
}
|
|
|
|
// initializes the variables as parameters
|
|
pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted
|
|
|
|
pStk1->IncState();
|
|
}
|
|
|
|
// finally execution of the found function
|
|
|
|
if ( !pStk3->GetRetVar( // puts the result on the stack
|
|
pt->m_block->Execute(pStk3) )) // GetRetVar said if it is interrupted
|
|
{
|
|
if ( !pStk3->IsOk() && pt->m_pProg != program )
|
|
{
|
|
pStk3->SetPosError(pToken); // indicates the error on the procedure call
|
|
}
|
|
return false; // interrupt !
|
|
}
|
|
|
|
return pStack->Return( pStk3 );
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotFunction::RestoreCall(CBotFunction* localFunctionList,
|
|
long &nIdent, const std::string &name, CBotVar** ppVars, CBotStack* pStack)
|
|
{
|
|
CBotTypResult type;
|
|
CBotFunction* pt = nullptr;
|
|
CBotStack* pStk1;
|
|
CBotStack* pStk3;
|
|
|
|
// search function to return the ok identifier
|
|
|
|
pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
|
|
|
|
if ( pt != nullptr )
|
|
{
|
|
pStk1 = pStack->RestoreStack(pt);
|
|
if ( pStk1 == nullptr ) return;
|
|
|
|
pStk1->SetProgram(pt->m_pProg); // it may have changed module
|
|
|
|
if ( pStk1->GetBlock() != CBotStack::BlockVisibilityType::FUNCTION)
|
|
{
|
|
CBotStack* pStk2 = pStk1->RestoreStack(nullptr); // used more
|
|
if ( pStk2 == nullptr ) return;
|
|
pStk3 = pStk2->RestoreStack(nullptr);
|
|
if ( pStk3 == nullptr ) return;
|
|
}
|
|
else
|
|
{
|
|
pStk3 = pStk1->RestoreStack(nullptr);
|
|
if ( pStk3 == nullptr ) return;
|
|
}
|
|
|
|
// preparing parameters on the stack
|
|
|
|
{
|
|
if ( !pt->m_MasterClass.empty() )
|
|
{
|
|
// CBotVar* pInstance = m_pProg->m_thisVar;
|
|
// make "this" known
|
|
CBotVar* pThis = pStk1->FindVar("this");
|
|
pThis->SetInit(CBotVar::InitType::IS_POINTER);
|
|
pThis->SetUniqNum(-2);
|
|
}
|
|
}
|
|
|
|
if ( pStk1->GetState() == 0 )
|
|
{
|
|
pt->m_param->RestoreState(pStk3, true);
|
|
return;
|
|
}
|
|
|
|
// initializes the variables as parameters
|
|
pt->m_param->RestoreState(pStk3, false);
|
|
pt->m_block->RestoreState(pStk3, true);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
int CBotFunction::DoCall(CBotFunction* localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
|
|
CBotVar** ppVars, CBotStack* pStack, CBotToken* pToken, CBotClass* pClass)
|
|
{
|
|
CBotTypResult type;
|
|
CBotProgram* pProgCurrent = pStack->GetProgram();
|
|
|
|
CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type, false);
|
|
|
|
if ( pt != nullptr )
|
|
{
|
|
// DEBUG( "CBotFunction::DoCall" + pt->GetName(), 0, pStack);
|
|
|
|
CBotStack* pStk = pStack->AddStack(pt, CBotStack::BlockVisibilityType::FUNCTION);
|
|
// if ( pStk == EOX ) return true;
|
|
|
|
pStk->SetProgram(pt->m_pProg); // it may have changed module
|
|
CBotStack* pStk3 = pStk->AddStack(nullptr, CBotStack::BlockVisibilityType::BLOCK); // to set parameters passed
|
|
|
|
// preparing parameters on the stack
|
|
|
|
if ( pStk->GetState() == 0 )
|
|
{
|
|
// sets the variable "this" on the stack
|
|
CBotVar* pthis = CBotVar::Create("this", CBotTypNullPointer);
|
|
pthis->Copy(pThis, false);
|
|
pthis->SetUniqNum(-2); // special value
|
|
pStk->AddVar(pthis);
|
|
|
|
CBotClass* pClass = pThis->GetClass()->GetParent();
|
|
if ( pClass )
|
|
{
|
|
// sets the variable "super" on the stack
|
|
CBotVar* psuper = CBotVar::Create("super", CBotTypNullPointer);
|
|
psuper->Copy(pThis, false); // in fact identical to "this"
|
|
psuper->SetUniqNum(-3); // special value
|
|
pStk->AddVar(psuper);
|
|
}
|
|
// initializes the variables as parameters
|
|
pt->m_param->Execute(ppVars, pStk3); // cannot be interrupted
|
|
pStk->IncState();
|
|
}
|
|
|
|
if ( pStk->GetState() == 1 )
|
|
{
|
|
if ( pt->m_bSynchro )
|
|
{
|
|
CBotProgram* pProgBase = pStk->GetProgram(true);
|
|
if ( !pClass->Lock(pProgBase) ) return false; // try to lock, interrupt if failed
|
|
}
|
|
pStk->IncState();
|
|
}
|
|
// finally calls the found function
|
|
|
|
if ( !pStk3->GetRetVar( // puts the result on the stack
|
|
pt->m_block->Execute(pStk3) )) // GetRetVar said if it is interrupted
|
|
{
|
|
if ( !pStk3->IsOk() )
|
|
{
|
|
if ( pt->m_bSynchro )
|
|
{
|
|
pClass->Unlock(); // release function
|
|
}
|
|
|
|
if ( pt->m_pProg != pProgCurrent )
|
|
{
|
|
pStk3->SetPosError(pToken); // indicates the error on the procedure call
|
|
}
|
|
}
|
|
return false; // interrupt !
|
|
}
|
|
|
|
if ( pt->m_bSynchro )
|
|
{
|
|
pClass->Unlock(); // release function
|
|
}
|
|
|
|
return pStack->Return( pStk3 );
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::RestoreCall(CBotFunction* localFunctionList, long &nIdent, const std::string &name, CBotVar* pThis,
|
|
CBotVar** ppVars, CBotStack* pStack, CBotClass* pClass)
|
|
{
|
|
CBotTypResult type;
|
|
CBotFunction* pt = FindLocalOrPublic(localFunctionList, nIdent, name, ppVars, type);
|
|
|
|
if ( pt != nullptr )
|
|
{
|
|
CBotStack* pStk = pStack->RestoreStack(pt);
|
|
if ( pStk == nullptr ) return true;
|
|
pStk->SetProgram(pt->m_pProg); // it may have changed module
|
|
|
|
CBotVar* pthis = pStk->FindVar("this");
|
|
pthis->SetUniqNum(-2);
|
|
|
|
if (pClass->GetParent() != nullptr)
|
|
{
|
|
CBotVar* psuper = pStk->FindVar("super");
|
|
if (psuper != nullptr) psuper->SetUniqNum(-3);
|
|
}
|
|
|
|
CBotStack* pStk3 = pStk->RestoreStack(nullptr); // to set parameters passed
|
|
if ( pStk3 == nullptr ) return true;
|
|
|
|
pt->m_param->RestoreState(pStk3, true); // parameters
|
|
|
|
if ( pStk->GetState() > 1 && // latching is effective?
|
|
pt->m_bSynchro )
|
|
{
|
|
CBotProgram* pProgBase = pStk->GetProgram(true);
|
|
pClass->Lock(pProgBase); // locks the class
|
|
}
|
|
|
|
// finally calls the found function
|
|
|
|
pt->m_block->RestoreState(pStk3, true); // interrupt !
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotFunction::CheckParam(CBotDefParam* pParam)
|
|
{
|
|
CBotDefParam* pp = m_param;
|
|
while ( pp != nullptr && pParam != nullptr )
|
|
{
|
|
CBotTypResult type1 = pp->GetTypResult();
|
|
CBotTypResult type2 = pParam->GetTypResult();
|
|
if ( !type1.Compare(type2) ) return false;
|
|
pp = pp->GetNext();
|
|
pParam = pParam->GetNext();
|
|
}
|
|
return ( pp == nullptr && pParam == nullptr );
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string CBotFunction::GetName()
|
|
{
|
|
return m_token.GetString();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string CBotFunction::GetParams()
|
|
{
|
|
if (m_param == nullptr ) return std::string("()");
|
|
|
|
std::string params = "( ";
|
|
CBotDefParam* p = m_param; // list of parameters
|
|
|
|
while (p != nullptr)
|
|
{
|
|
params += p->GetParamString();
|
|
p = p->GetNext();
|
|
if ( p != nullptr ) params += ", ";
|
|
}
|
|
|
|
params += " )";
|
|
return params;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotFunction* CBotFunction::Next()
|
|
{
|
|
return m_next;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotFunction::AddPublic(CBotFunction* func)
|
|
{
|
|
m_publicFunctions.insert(func);
|
|
}
|
|
|
|
std::string CBotFunction::GetDebugData()
|
|
{
|
|
std::stringstream ss;
|
|
if (IsPublic()) ss << "public ";
|
|
if (IsExtern()) ss << "extern ";
|
|
ss << GetName() << GetParams();
|
|
//ss << "FuncID = " << m_nFuncIdent;
|
|
return ss.str();
|
|
}
|
|
|
|
std::map<std::string, CBotInstr*> CBotFunction::GetDebugLinks()
|
|
{
|
|
auto links = CBotInstr::GetDebugLinks();
|
|
links["m_block"] = m_block;
|
|
return links;
|
|
}
|
|
|
|
} // namespace CBot
|