colobot/src/CBot/CBotInstr/CBotInstrMethode.cpp

357 lines
11 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 <sstream>
#include "CBot/CBotInstr/CBotInstrMethode.h"
#include "CBot/CBotInstr/CBotExprRetVar.h"
#include "CBot/CBotInstr/CBotInstrUtils.h"
#include "CBot/CBotStack.h"
#include "CBot/CBotCStack.h"
#include "CBot/CBotClass.h"
#include "CBot/CBotVar/CBotVar.h"
namespace CBot
{
////////////////////////////////////////////////////////////////////////////////
CBotInstrMethode::CBotInstrMethode()
{
m_parameters = nullptr;
m_MethodeIdent = 0;
}
////////////////////////////////////////////////////////////////////////////////
CBotInstrMethode::~CBotInstrMethode()
{
delete m_parameters;
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotInstrMethode::Compile(CBotToken* &p, CBotCStack* pStack, CBotVar* var, bool bMethodChain)
{
CBotInstrMethode* inst = new CBotInstrMethode();
inst->SetToken(p); // corresponding token
CBotToken* pp = p;
p = p->GetNext();
if (p->GetType() == ID_OPENPAR)
{
inst->m_methodName = pp->GetString();
// compiles the list of parameters
CBotVar* ppVars[1000];
inst->m_parameters = CompileParams(p, pStack, ppVars);
if (pStack->IsOk())
{
inst->m_thisIdent = var->GetUniqNum();
CBotClass* pClass = var->GetClass(); // pointer to the class
inst->m_className = pClass->GetName(); // name of the class
CBotTypResult r = pClass->CompileMethode(inst->m_methodName, var, ppVars,
pStack, inst->m_MethodeIdent);
delete pStack->TokenStack(); // release parameters on the stack
inst->m_typRes = r;
if (inst->m_typRes.GetType() > 20)
{
pStack->SetError(static_cast<CBotError>(inst->m_typRes.GetType()), pp);
delete inst;
return nullptr;
}
// put the result on the stack to have something
if (inst->m_typRes.GetType() > 0)
{
CBotVar* pResult = CBotVar::Create("", inst->m_typRes);
if (inst->m_typRes.Eq(CBotTypClass))
{
pResult->SetClass(inst->m_typRes.GetClass());
}
pStack->SetVar(pResult);
}
else pStack->SetVar(nullptr);
pp = p;
if (nullptr != (inst->m_exprRetVar = CBotExprRetVar::Compile(p, pStack, bMethodChain)))
{
inst->m_exprRetVar->SetToken(pp);
delete pStack->TokenStack();
}
if ( pStack->IsOk() )
return inst;
}
delete inst;
return nullptr;
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
bool CBotInstrMethode::ExecuteVar(CBotVar* &pVar, CBotStack* &pj, CBotToken* prevToken, bool bStep, bool bExtend)
{
CBotVar* ppVars[1000];
CBotStack* pile1 = pj->AddStack(this, CBotStack::BlockVisibilityType::BLOCK); // a place for the copy of This
if (pVar->GetPointer() == nullptr)
{
pj->SetError(CBotErrNull, prevToken);
return pj->Return(pile1);
}
CBotStack* pile3 = nullptr;
if (m_exprRetVar != nullptr) // .func().member
{
pile3 = pile1->AddStack2();
if (pile3->GetState() == 1)
{
if (!m_exprRetVar->Execute(pile3)) return false;
pVar = nullptr;
return pj->Return(pile3);
}
}
if (pile1->IfStep()) return false;
CBotStack* pile2 = pile1->AddStack(); // for the next parameters
if ( pile1->GetState() == 0)
{
CBotVar* pThis = CBotVar::Create(pVar);
pThis->Copy(pVar);
// this value should be taken before the evaluation parameters
// Test.Action (Test = Other);
// action must act on the value before test = Other!
pThis->SetName("this");
pThis->SetUniqNum(-2);
pile1->SetVar(pThis);
pile1->IncState();
}
int i = 0;
CBotInstr* p = m_parameters;
// evaluate the parameters
// and places the values on the stack
// to be interrupted at any time
if (p != nullptr) while ( true)
{
if (pile2->GetState() == 0)
{
if (!p->Execute(pile2)) return false; // interrupted here?
if (!pile2->SetState(1)) return false; // special mark to recognize parameters
}
ppVars[i++] = pile2->GetVar(); // construct the list of pointers
pile2 = pile2->AddStack(); // space on the stack for the result
p = p->GetNext();
if ( p == nullptr) break;
}
ppVars[i] = nullptr;
CBotVar* pThis = pile1->GetVar();
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
CBotVar* pResult = nullptr;
if (m_typRes.GetType() > 0) pResult = CBotVar::Create("", m_typRes);
if (m_typRes.Eq(CBotTypClass))
{
pResult->SetClass(m_typRes.GetClass());
}
CBotVar* pRes = pResult;
if ( !pClass->ExecuteMethode(m_MethodeIdent, m_methodName,
pThis, ppVars,
pResult, pile2, GetToken())) return false;
if (pRes != pResult) delete pRes;
if (m_exprRetVar != nullptr) // .func().member
{
pile3->SetCopyVar( pile2->GetVar() );
pile2->SetVar(nullptr);
pile3->SetState(1); // set call is done
pVar = nullptr;
return false; // go back to the top ^^^
}
pVar = nullptr; // does not return value for this
return pj->Return(pile2); // release the entire stack
}
////////////////////////////////////////////////////////////////////////////////
void CBotInstrMethode::RestoreStateVar(CBotStack* &pile, bool bMain)
{
if (!bMain) return;
CBotVar* ppVars[1000];
CBotStack* pile1 = pile->RestoreStack(this); // place for the copy of This
if (pile1 == nullptr) return;
if (m_exprRetVar != nullptr) // .func().member
{
CBotStack* pile3 = pile1->AddStack2();
if (pile3->GetState() == 1) // function call is done?
{
m_exprRetVar->RestoreState(pile3, bMain);
return;
}
}
CBotStack* pile2 = pile1->RestoreStack(); // and for the parameters coming
if (pile2 == nullptr) return;
CBotVar* pThis = pile1->GetVar();
assert(pThis != nullptr); // see fix for issues #256 & #436
pThis->SetUniqNum(-2);
int i = 0;
CBotInstr* p = m_parameters;
// evaluate the parameters
// and places the values on the stack
// to be interrupted at any time
if (p != nullptr) while ( true)
{
if (pile2->GetState() == 0)
{
p->RestoreState(pile2, true); // interrupted here!
return;
}
ppVars[i++] = pile2->GetVar(); // construct the list of pointers
pile2 = pile2->RestoreStack();
if (pile2 == nullptr) return;
p = p->GetNext();
if ( p == nullptr) break;
}
ppVars[i] = nullptr;
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
// CBotVar* pResult = nullptr;
// CBotVar* pRes = pResult;
pClass->RestoreMethode(m_MethodeIdent, m_methodName,
pThis, ppVars, pile2);
}
////////////////////////////////////////////////////////////////////////////////
bool CBotInstrMethode::Execute(CBotStack* &pj)
{
CBotVar* ppVars[1000];
CBotStack* pile1 = pj->AddStack(this, CBotStack::BlockVisibilityType::BLOCK); // place for the copy of This
if (pile1->IfStep()) return false;
CBotStack* pile2 = pile1->AddStack(); // and for the parameters coming
if ( pile1->GetState() == 0)
{
CBotVar* pThis = pile1->CopyVar(m_token);
// this value should be taken before the evaluation parameters
// Test.Action (Test = Other);
// Action must act on the value before test = Other!
pThis->SetName("this");
pile1->SetVar(pThis);
pile1->IncState();
}
int i = 0;
CBotInstr* p = m_parameters;
// evaluate the parameters
// and places the values on the stack
// to be interrupted at any time
if (p != nullptr) while ( true)
{
if (pile2->GetState() == 0)
{
if (!p->Execute(pile2)) return false; // interrupted here?
if (!pile2->SetState(1)) return false; // special mark to recognize parameters
}
ppVars[i++] = pile2->GetVar(); // construct the list of pointers
pile2 = pile2->AddStack(); // space on the stack for the results
p = p->GetNext();
if ( p == nullptr) break;
}
ppVars[i] = nullptr;
CBotVar* pThis = pile1->GetVar();
CBotClass* pClass;
if (m_thisIdent == -3) // super.method()
pClass = CBotClass::Find(m_className);
else
pClass = pThis->GetClass();
CBotVar* pResult = nullptr;
if (m_typRes.GetType()>0) pResult = CBotVar::Create("", m_typRes);
if (m_typRes.Eq(CBotTypClass))
{
pResult->SetClass(m_typRes.GetClass());
}
CBotVar* pRes = pResult;
if ( !pClass->ExecuteMethode(m_MethodeIdent, m_methodName,
pThis, ppVars,
pResult, pile2, GetToken())) return false; // interupted
// set the new value of this in place of the old variable
CBotVar* old = pile1->FindVar(m_token, false);
old->Copy(pThis, false);
if (pRes != pResult) delete pRes;
return pj->Return(pile2); // release the entire stack
}
std::string CBotInstrMethode::GetDebugData()
{
std::stringstream ss;
ss << m_methodName << std::endl;
ss << "MethodID = " << m_MethodeIdent << std::endl;
ss << "result = " << m_typRes.ToString();
return ss.str();
}
std::map<std::string, CBotInstr*> CBotInstrMethode::GetDebugLinks()
{
auto links = CBotInstr::GetDebugLinks();
links["m_parameters"] = m_parameters;
return links;
}
} // namespace CBot