476 lines
14 KiB
C++
476 lines
14 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2020, 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/CBotVar/CBotVarClass.h"
|
|
|
|
#include "CBot/CBotClass.h"
|
|
#include "CBot/CBotStack.h"
|
|
#include "CBot/CBotDefines.h"
|
|
|
|
#include "CBot/CBotInstr/CBotInstr.h"
|
|
|
|
#include <cassert>
|
|
|
|
namespace CBot
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::set<CBotVarClass*> CBotVarClass::m_instances{};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVarClass::CBotVarClass(const CBotToken& name, const CBotTypResult& type) : CBotVar(name)
|
|
{
|
|
if ( !type.Eq(CBotTypClass) &&
|
|
!type.Eq(CBotTypIntrinsic) && // by convenience there accepts these types
|
|
!type.Eq(CBotTypPointer) &&
|
|
!type.Eq(CBotTypArrayPointer) &&
|
|
!type.Eq(CBotTypArrayBody)) assert(0);
|
|
|
|
m_next = nullptr;
|
|
m_pMyThis = nullptr;
|
|
m_pUserPtr = OBJECTCREATED;//nullptr;
|
|
m_InitExpr = nullptr;
|
|
m_LimExpr = nullptr;
|
|
m_pVar = nullptr;
|
|
m_type = type;
|
|
if ( type.Eq(CBotTypArrayPointer) ) m_type.SetType( CBotTypArrayBody );
|
|
else if ( !type.Eq(CBotTypArrayBody) ) m_type.SetType( CBotTypClass );
|
|
// official type for this object
|
|
|
|
m_pClass = nullptr;
|
|
m_binit = InitType::UNDEF;
|
|
m_bStatic = false;
|
|
m_mPrivate = ProtectionLevel::Public;
|
|
m_bConstructor = false;
|
|
m_CptUse = 0;
|
|
m_ItemIdent = type.Eq(CBotTypIntrinsic) ? 0 : CBotVar::NextUniqNum();
|
|
|
|
// add to the list
|
|
if (m_ItemIdent != 0) m_instances.insert(this);
|
|
|
|
CBotClass* pClass = type.GetClass();
|
|
|
|
SetClass( pClass );
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVarClass::~CBotVarClass( )
|
|
{
|
|
if ( m_CptUse != 0 )
|
|
assert(0);
|
|
|
|
// removes the class list
|
|
if (m_ItemIdent != 0) m_instances.erase(this);
|
|
|
|
delete m_pVar;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::ConstructorSet()
|
|
{
|
|
m_bConstructor = true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::Copy(CBotVar* pSrc, bool bName)
|
|
{
|
|
pSrc = pSrc->GetPointer(); // if source given by a pointer
|
|
|
|
if ( pSrc->GetType() != CBotTypClass )
|
|
assert(0);
|
|
|
|
CBotVarClass* p = static_cast<CBotVarClass*>(pSrc);
|
|
|
|
if (bName) *m_token = *p->m_token;
|
|
|
|
m_type = p->m_type;
|
|
m_binit = p->m_binit;
|
|
//- m_bStatic = p->m_bStatic;
|
|
m_pClass = p->m_pClass;
|
|
|
|
// m_next = nullptr;
|
|
m_pUserPtr = p->m_pUserPtr;
|
|
m_pMyThis = nullptr;//p->m_pMyThis;
|
|
m_ItemIdent = p->m_ItemIdent;
|
|
|
|
// keeps indentificator the same (by default)
|
|
if (m_ident == 0 ) m_ident = p->m_ident;
|
|
|
|
delete m_pVar;
|
|
m_pVar = nullptr;
|
|
|
|
CBotVar* pv = p->m_pVar;
|
|
while( pv != nullptr )
|
|
{
|
|
CBotVar* pn = CBotVar::Create(pv);
|
|
pn->Copy( pv );
|
|
if ( m_pVar == nullptr ) m_pVar = pn;
|
|
else m_pVar->AddNext(pn);
|
|
|
|
pv = pv->GetNext();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::SetIdent(long n)
|
|
{
|
|
m_ItemIdent = n;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::SetClass(CBotClass* pClass)//, int &nIdent)
|
|
{
|
|
m_type.m_class = pClass;
|
|
|
|
if ( m_pClass == pClass ) return;
|
|
|
|
m_pClass = pClass;
|
|
|
|
// initializes the variables associated with this class
|
|
delete m_pVar;
|
|
m_pVar = nullptr;
|
|
|
|
if (pClass == nullptr) return;
|
|
|
|
CBotVar* pv = nullptr;
|
|
while (pClass != nullptr)
|
|
{
|
|
if ( pv == nullptr ) pv = pClass->GetVar();
|
|
if ( pv == nullptr ) { pClass = pClass->GetParent(); continue; }
|
|
// seeks the maximum dimensions of the table
|
|
CBotInstr* p = pv->m_LimExpr; // the different formulas
|
|
if ( p != nullptr )
|
|
{
|
|
CBotStack* pile = CBotStack::AllocateStack(); // an independent stack
|
|
int n = 0;
|
|
int max[100];
|
|
|
|
while (p != nullptr)
|
|
{
|
|
while( pile->IsOk() && !p->Execute(pile) ) ; // calculate size without interruptions
|
|
CBotVar* v = pile->GetVar(); // result
|
|
max[n] = v->GetValInt(); // value
|
|
n++;
|
|
p = p->GetNext3();
|
|
}
|
|
while (n<100) max[n++] = 0;
|
|
|
|
pv->m_type.SetArray(max); // stores the limitations
|
|
pile->Delete();
|
|
}
|
|
|
|
CBotVar* pn = CBotVar::Create( pv ); // a copy
|
|
pn->SetStatic(pv->IsStatic());
|
|
pn->SetPrivate(pv->GetPrivate());
|
|
|
|
if ( pv->m_InitExpr != nullptr ) // expression for initialization?
|
|
{
|
|
#if STACKMEM
|
|
CBotStack* pile = CBotStack::AllocateStack(); // an independent stack
|
|
|
|
while(pile->IsOk() && !pv->m_InitExpr->Execute(pile, pn)); // evaluates the expression without timer
|
|
|
|
pile->Delete();
|
|
#else
|
|
CBotStack* pile = new CBotStack(nullptr); // an independent stack
|
|
while(!pv->m_InitExpr->Execute(pile)); // evaluates the expression without timer
|
|
pn->SetVal( pile->GetVar() ) ;
|
|
delete pile;
|
|
#endif
|
|
}
|
|
|
|
// pn->SetUniqNum(CBotVar::NextUniqNum()); // enumerate elements
|
|
pn->SetUniqNum(pv->GetUniqNum()); //++nIdent
|
|
pn->m_pMyThis = this;
|
|
|
|
if ( m_pVar == nullptr) m_pVar = pn;
|
|
else m_pVar->AddNext( pn );
|
|
pv = pv->GetNext();
|
|
if ( pv == nullptr ) pClass = pClass->GetParent();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotClass* CBotVarClass::GetClass()
|
|
{
|
|
return m_pClass;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::Update(void* pUser)
|
|
{
|
|
// retrieves the user pointer according to the class
|
|
// or according to the parameter passed to CBotProgram::Run()
|
|
|
|
if ( m_pUserPtr != nullptr) pUser = m_pUserPtr;
|
|
if ( pUser == OBJECTDELETED ||
|
|
pUser == OBJECTCREATED ) return;
|
|
m_pClass->Update(this, pUser);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVar* CBotVarClass::GetItem(const std::string& name)
|
|
{
|
|
CBotVar* p = m_pVar;
|
|
|
|
while ( p != nullptr )
|
|
{
|
|
if ( p->GetName() == name ) return p;
|
|
p = p->GetNext();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVar* CBotVarClass::GetItemRef(int nIdent)
|
|
{
|
|
CBotVar* p = m_pVar;
|
|
|
|
while ( p != nullptr )
|
|
{
|
|
if ( p->GetUniqNum() == nIdent ) return p;
|
|
p = p->GetNext();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVar* CBotVarClass::GetItem(int n, bool bExtend)
|
|
{
|
|
CBotVar* p = m_pVar;
|
|
|
|
if ( n < 0 ) return nullptr;
|
|
if ( n > MAXARRAYSIZE ) return nullptr;
|
|
|
|
if ( m_type.GetLimite() >= 0 && n >= m_type.GetLimite() ) return nullptr;
|
|
|
|
if ( p == nullptr && bExtend )
|
|
{
|
|
p = CBotVar::Create("", m_type.GetTypElem());
|
|
m_pVar = p;
|
|
}
|
|
|
|
if ( n == 0 ) return p;
|
|
|
|
while ( n-- > 0 )
|
|
{
|
|
if ( p->m_next == nullptr )
|
|
{
|
|
if ( bExtend ) p->m_next = CBotVar::Create("", m_type.GetTypElem());
|
|
if ( p->m_next == nullptr ) return nullptr;
|
|
}
|
|
p = p->m_next;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVar* CBotVarClass::GetItemList()
|
|
{
|
|
return m_pVar;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
std::string CBotVarClass::GetValString()
|
|
{
|
|
std::string res;
|
|
|
|
if ( m_pClass != nullptr ) // not used for an array
|
|
{
|
|
res = m_pClass->GetName() + std::string("( ");
|
|
|
|
CBotClass* pClass = m_pClass;
|
|
long prevID = 0;
|
|
{
|
|
CBotVar* pv = m_pVar;
|
|
if (pv != nullptr) while (true)
|
|
{
|
|
if (pv->GetUniqNum() < prevID)
|
|
{
|
|
pClass = pClass->GetParent();
|
|
if (pClass == nullptr) break;
|
|
res += " ) extends ";
|
|
res += pClass->GetName();
|
|
res += "( ";
|
|
if (pClass->GetVar() == nullptr) continue;
|
|
}
|
|
|
|
prevID = pv->GetUniqNum();
|
|
|
|
res += pv->GetName() + std::string("=");
|
|
|
|
if ( pv->IsStatic() )
|
|
{
|
|
res += pClass->GetItemRef(prevID)->GetValString();
|
|
}
|
|
else
|
|
{
|
|
res += pv->GetValString();
|
|
}
|
|
pv = pv->GetNext();
|
|
if ( pv == nullptr ) break;
|
|
if ( pv->GetUniqNum() > prevID ) res += ", ";
|
|
}
|
|
|
|
if (pClass != nullptr) while (true)
|
|
{
|
|
pClass = pClass->GetParent();
|
|
if (pClass == nullptr) break;
|
|
res += " ) extends ";
|
|
res += pClass->GetName();
|
|
res += "( ";
|
|
}
|
|
}
|
|
|
|
res += " )";
|
|
}
|
|
else
|
|
{
|
|
res = "{ ";
|
|
|
|
CBotVar* pv = m_pVar;
|
|
while ( pv != nullptr )
|
|
{
|
|
res += pv->GetValString();
|
|
if ( pv->GetNext() != nullptr ) res += ", ";
|
|
pv = pv->GetNext();
|
|
}
|
|
|
|
res += " }";
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::IncrementUse()
|
|
{
|
|
m_CptUse++;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void CBotVarClass::DecrementUse()
|
|
{
|
|
m_CptUse--;
|
|
if ( m_CptUse == 0 )
|
|
{
|
|
// if there is one, call the destructor
|
|
// but only if a constructor had been called.
|
|
if ( m_bConstructor )
|
|
{
|
|
m_CptUse++; // does not return to the destructor
|
|
|
|
// m_error is static in the stack
|
|
// saves the value for return
|
|
CBotError err;
|
|
int start, end;
|
|
CBotStack* pile = nullptr;
|
|
err = pile->GetError(start,end); // stack == nullptr it does not bother!
|
|
|
|
pile = CBotStack::AllocateStack(); // clears the error
|
|
CBotVar* ppVars[1];
|
|
ppVars[0] = nullptr;
|
|
|
|
CBotVar* pThis = CBotVar::Create("this", CBotTypNullPointer);
|
|
pThis->SetPointer(this);
|
|
|
|
std::string nom = std::string("~") + m_pClass->GetName();
|
|
long ident = 0;
|
|
|
|
CBotToken token(nom); // TODO
|
|
|
|
while ( pile->IsOk() && !m_pClass->ExecuteMethode(ident, pThis, ppVars, CBotTypResult(CBotTypVoid), pile, &token)) ; // waits for the end
|
|
|
|
pile->ResetError(err, start,end);
|
|
|
|
pile->Delete();
|
|
delete pThis;
|
|
m_CptUse--;
|
|
}
|
|
|
|
delete this; // self-destructs!
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVarClass* CBotVarClass::GetPointer()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
CBotVarClass* CBotVarClass::Find(long id)
|
|
{
|
|
for (CBotVarClass* p : m_instances)
|
|
{
|
|
if (p->m_ItemIdent == id) return p;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotVarClass::Eq(CBotVar* left, CBotVar* right)
|
|
{
|
|
CBotVar* l = left->GetItemList();
|
|
CBotVar* r = right->GetItemList();
|
|
|
|
while ( l != nullptr && r != nullptr )
|
|
{
|
|
if ( l->Ne(l, r) ) return false;
|
|
l = l->GetNext();
|
|
r = r->GetNext();
|
|
}
|
|
|
|
// should always arrived simultaneously at the end (same classes)
|
|
return l == r;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotVarClass::Ne(CBotVar* left, CBotVar* right)
|
|
{
|
|
CBotVar* l = left->GetItemList();
|
|
CBotVar* r = right->GetItemList();
|
|
|
|
while ( l != nullptr && r != nullptr )
|
|
{
|
|
if ( l->Ne(l, r) ) return true;
|
|
l = l->GetNext();
|
|
r = r->GetNext();
|
|
}
|
|
|
|
// should always arrived simultaneously at the end (same classes)
|
|
return l != r;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool CBotVarClass::Save1State(std::ostream &ostr)
|
|
{
|
|
if (!WriteType(ostr, m_type)) return false;
|
|
if (!WriteLong(ostr, m_ItemIdent)) return false;
|
|
|
|
return SaveVars(ostr, m_pVar); // content of the object
|
|
}
|
|
|
|
} // namespace CBot
|