/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2021, 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/CBotCStack.h" #include "CBot/CBotClass.h" #include "CBot/CBotToken.h" #include "CBot/CBotExternalCall.h" #include "CBot/CBotVar/CBotVar.h" #include "CBot/CBotInstr/CBotFunction.h" namespace CBot { struct CBotCStack::Data { //! The program currently being compiled CBotProgram* prog = nullptr; //! The current error state of the compile stack CBotError error = CBotNoErr; int errEnd = 0; //! The return type of the function currently being compiled CBotTypResult retTyp = CBotTypResult(CBotTypVoid); }; CBotCStack::CBotCStack(CBotCStack* ppapa) { m_prev = ppapa; if (ppapa == nullptr) { m_data = new CBotCStack::Data; m_errStart = 0; m_bBlock = true; } else { m_data = ppapa->m_data; m_errStart = ppapa->m_errStart; m_bBlock = false; } } //////////////////////////////////////////////////////////////////////////////// CBotCStack::~CBotCStack() { if (m_prev == nullptr) delete m_data; } //////////////////////////////////////////////////////////////////////////////// CBotCStack* CBotCStack::TokenStack(CBotToken* pToken, bool bBlock) { if (m_next) return m_next.get(); // include on an existing stack m_next.reset(new CBotCStack(this)); m_next->m_bBlock = bBlock; if (pToken != nullptr) m_next->SetStartError(pToken->GetStart()); return m_next.get(); } void CBotCStack::DeleteNext() { m_next.reset(); } CBotInstr* CBotCStack::Return(CBotInstr* inst, CBotCStack* pfils) { if ( pfils == this ) return inst; m_var = std::move(pfils->m_var); // result transmitted if (m_data->error != CBotNoErr) { m_errStart = pfils->m_errStart; // retrieves the position of the error } m_next.reset(); return inst; } //////////////////////////////////////////////////////////////////////////////// CBotFunction* CBotCStack::ReturnFunc(CBotFunction* inst, CBotCStack* pfils) { m_var = std::move(pfils->m_var); // result transmitted if (m_data->error != CBotNoErr) { m_errStart = pfils->m_errStart; // retrieves the position of the error } m_next.reset(); return inst; } //////////////////////////////////////////////////////////////////////////////// CBotError CBotCStack::GetError(int& start, int& end) { start = m_errStart; end = m_data->errEnd; return m_data->error; } //////////////////////////////////////////////////////////////////////////////// CBotError CBotCStack::GetError() { return m_data->error; } //////////////////////////////////////////////////////////////////////////////// CBotTypResult CBotCStack::GetTypResult(CBotVar::GetTypeMode mode) { if (m_var == nullptr) return CBotTypResult(99); return m_var->GetTypResult(mode); } //////////////////////////////////////////////////////////////////////////////// int CBotCStack::GetType(CBotVar::GetTypeMode mode) { if (m_var == nullptr) return 99; return m_var->GetType(mode); } //////////////////////////////////////////////////////////////////////////////// CBotClass* CBotCStack::GetClass() { if ( m_var == nullptr ) return nullptr; if ( m_var->GetType(CBotVar::GetTypeMode::CLASS_AS_POINTER) != CBotTypPointer ) return nullptr; return m_var->GetClass(); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetType(CBotTypResult& type) { if (m_var == nullptr) return; m_var->SetType( type ); } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotCStack::FindVar(CBotToken* &pToken) { CBotCStack* p = this; const auto& name = pToken->GetString(); while (p != nullptr) { if (p->m_bBlock) for (auto& var : p->m_listVar) { if (name == var->GetName()) return var.get(); } p = p->m_prev; } return nullptr; } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotCStack::FindVar(CBotToken& Token) { CBotToken* pt = &Token; return FindVar(pt); } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotCStack::CopyVar(CBotToken& Token) { CBotVar* pVar = FindVar( Token ); if ( pVar == nullptr) return nullptr; CBotVar* pCopy = CBotVar::Create( "", pVar->GetType() ); pCopy->Copy(pVar); return pCopy; } //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::IsOk() { return (m_data->error == CBotNoErr); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetStartError( int pos ) { if (m_data->error != CBotNoErr) return; // does not change existing error m_errStart = pos; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetError(CBotError n, int pos) { if (n != CBotNoErr && m_data->error != CBotNoErr) return; // does not change existing error m_data->error = n; m_data->errEnd = pos; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetError(CBotError n, CBotToken* p) { if (m_data->error != CBotNoErr) return; // does not change existing error m_data->error = n; m_errStart = p->GetStart(); m_data->errEnd = p->GetEnd(); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::ResetError(CBotError n, int start, int end) { m_data->error = n; m_errStart = start; m_data->errEnd = end; } //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::NextToken(CBotToken* &p) { CBotToken* pp = p; p = p->GetNext(); if (p!=nullptr) return true; SetError(CBotErrNoTerminator, pp->GetEnd()); return false; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetProgram(CBotProgram* p) { m_data->prog = p; } //////////////////////////////////////////////////////////////////////////////// CBotProgram* CBotCStack::GetProgram() { return m_data->prog; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetRetType(CBotTypResult& type) { m_data->retTyp = type; } //////////////////////////////////////////////////////////////////////////////// CBotTypResult CBotCStack::GetRetType() { return m_data->retTyp; } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetVar( CBotVar* var ) { m_var.reset(var); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::SetCopyVar( CBotVar* var ) { m_var.reset(); if ( var == nullptr ) return; m_var.reset(CBotVar::Create("", var->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC))); m_var->Copy( var ); } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotCStack::GetVar() { return m_var.get(); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::AddVar(CBotVar* pVar) { CBotCStack* p = this; // find the level of the current block while (p != nullptr && p->m_bBlock == 0) p = p->m_prev; if (p == nullptr || pVar == nullptr) return; p->m_listVar.emplace_back(pVar); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::CreateVarThis(CBotClass* pClass) { if ( pClass == nullptr ) return; CBotVar* pThis = CBotVar::Create("this", CBotTypResult(CBotTypClass, pClass)); pThis->SetUniqNum(-2); // special ID for "this" AddVar(pThis); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::CreateVarSuper(CBotClass* pClass) { if ( pClass == nullptr ) return; CBotVar* pSuper = CBotVar::Create("super", CBotTypResult(CBotTypClass, pClass)); pSuper->SetUniqNum(-3); // special ID for "super" AddVar(pSuper); } //////////////////////////////////////////////////////////////////////////////// void CBotCStack::CreateMemberVars(CBotClass* pClass, bool setDefined) { while (pClass != nullptr) { CBotVar* pv = pClass->GetVar(); while (pv != nullptr) { CBotVar* pcopy = CBotVar::Create(pv); CBotVar::InitType initType = CBotVar::InitType::UNDEF; if (setDefined || pv->IsStatic()) initType = CBotVar::InitType::DEF; pcopy->SetInit(initType); pcopy->SetUniqNum(pv->GetUniqNum()); pcopy->SetPrivate(pv->GetPrivate()); AddVar(pcopy); pv = pv->GetNext(); } pClass = pClass->GetParent(); } } //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::CheckVarLocal(CBotToken* &pToken) { CBotCStack* p = this; const auto& name = pToken->GetString(); // find the level of the current block while (p != nullptr && p->m_bBlock == 0) p = p->m_prev; if (p != nullptr) for (auto& var : p->m_listVar) { if (name == var->GetName()) return true; } return false; } //////////////////////////////////////////////////////////////////////////////// CBotTypResult CBotCStack::CompileCall(CBotToken* &p, CBotVar** ppVars, long& nIdent) { nIdent = 0; CBotTypResult val(-1); val = GetProgram()->GetExternalCalls()->CompileCall(p, nullptr, ppVars, this); if (val.GetType() < 0) { val = CBotFunction::CompileCall(p->GetString(), ppVars, nIdent, GetProgram()); if ( val.GetType() < 0 ) { // pVar = nullptr; // the error is not on a particular parameter SetError( static_cast(-val.GetType()), p ); val.SetType(-val.GetType()); return val; } } return val; } //////////////////////////////////////////////////////////////////////////////// bool CBotCStack::CheckCall(CBotToken* &pToken, CBotDefParam* pParam, const std::string& className) { const auto& name = pToken->GetString(); if ( GetProgram()->GetExternalCalls()->CheckCall(name) ) return true; for (CBotFunction* pp : GetProgram()->GetFunctions()) { if ( name == pp->GetName() ) { // ignore methods for a different class if ( className != pp->GetClassName() ) continue; // are parameters exactly the same? if ( pp->CheckParam( pParam ) ) return true; } } for (CBotFunction* pp : CBotFunction::m_publicFunctions) { if ( name == pp->GetName() ) { // ignore methods for a different class if ( className != pp->GetClassName() ) continue; // are parameters exactly the same? if ( pp->CheckParam( pParam ) ) return true; } } return false; } } // namespace CBot