/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2015, 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 */ // Modules inlcude #include "CBot/CBotVar/CBotVar.h" #include "CBot/CBotCall.h" #include "CBot/CBotStack.h" #include "CBot/CBotCStack.h" #include "CBot/CBotClass.h" #include "CBot/CBotUtils.h" #include "CBot/CBotFileUtils.h" #include "CBot/CBotInstr/CBotFunction.h" #include "CBotKeywordStrings.h" #include "CBot/stdlib/stdlib.h" // Local include // Global include #include //////////////////////////////////////////////////////////////////////////////// CBotProgram::CBotProgram() { m_Prog = nullptr; m_pRun = nullptr; m_pClass = nullptr; m_pStack = nullptr; m_pInstance = nullptr; m_ErrorCode = 0; m_Ident = 0; } //////////////////////////////////////////////////////////////////////////////// CBotProgram::CBotProgram(CBotVar* pInstance) { m_Prog = nullptr; m_pRun = nullptr; m_pClass = nullptr; m_pStack = nullptr; m_pInstance = pInstance; m_ErrorCode = 0; m_Ident = 0; } //////////////////////////////////////////////////////////////////////////////// CBotProgram::~CBotProgram() { // delete m_pClass; m_pClass->Purge(); m_pClass = nullptr; CBotClass::FreeLock(this); delete m_Prog; #if STACKMEM m_pStack->Delete(); #else delete m_pStack; #endif } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::Compile(const std::string& program, std::vector& ListFonctions, void* pUser) { int error = 0; Stop(); // delete m_pClass; m_pClass->Purge(); // purge the old definitions of classes // but without destroying the object m_pClass = nullptr; delete m_Prog; m_Prog= nullptr; ListFonctions.clear(); m_ErrorCode = 0; // transforms the program in Tokens CBotToken* pBaseToken = CBotToken::CompileTokens(program, error); if ( pBaseToken == nullptr ) return false; CBotCStack* pStack = new CBotCStack(nullptr); CBotToken* p = pBaseToken->GetNext(); // skips the first token (separator) pStack->SetBotCall(this); // defined used routines CBotCall::SetPUser(pUser); // first made a quick pass just to take the headers of routines and classes while ( pStack->IsOk() && p != nullptr && p->GetType() != 0) { if ( IsOfType(p, ID_SEP) ) continue; // semicolons lurking if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { CBotClass* nxt = CBotClass::Compile1(p, pStack); if (m_pClass == nullptr ) m_pClass = nxt; else m_pClass->AddNext(nxt); } else { CBotFunction* next = CBotFunction::Compile1(p, pStack, nullptr); if (m_Prog == nullptr ) m_Prog = next; else m_Prog->AddNext(next); } } if ( !pStack->IsOk() ) { m_ErrorCode = pStack->GetError(m_ErrorStart, m_ErrorEnd); delete m_Prog; m_Prog = nullptr; delete pBaseToken; return false; } // CBotFunction* temp = nullptr; CBotFunction* next = m_Prog; // rewind the list p = pBaseToken->GetNext(); // returns to the beginning while ( pStack->IsOk() && p != nullptr && p->GetType() != 0 ) { if ( IsOfType(p, ID_SEP) ) continue; // semicolons lurking if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { m_bCompileClass = true; CBotClass::Compile(p, pStack); // completes the definition of the class } else { m_bCompileClass = false; CBotFunction::Compile(p, pStack, next); if (next->IsExtern()) ListFonctions.push_back(next->GetName()/* + next->GetParams()*/); next->m_pProg = this; // keeps pointers to the module next = next->Next(); } } // delete m_Prog; // the list of first pass // m_Prog = temp; // list of the second pass if ( !pStack->IsOk() ) { m_ErrorCode = pStack->GetError(m_ErrorStart, m_ErrorEnd); delete m_Prog; m_Prog = nullptr; } delete pBaseToken; delete pStack; return (m_Prog != nullptr); } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::Start(const std::string& name) { #if STACKMEM m_pStack->Delete(); #else delete m_pStack; #endif m_pStack = nullptr; m_pRun = m_Prog; while (m_pRun != nullptr) { if ( m_pRun->GetName() == name ) break; m_pRun = m_pRun->m_next; } if ( m_pRun == nullptr ) { m_ErrorCode = CBotErrNoRun; return false; } #if STACKMEM m_pStack = CBotStack::FirstStack(); #else m_pStack = new CBotStack(nullptr); // creates an execution stack #endif m_pStack->SetBotCall(this); // bases for routines return true; // we are ready for Run () } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::GetPosition(const std::string& name, int& start, int& stop, CBotGet modestart, CBotGet modestop) { CBotFunction* p = m_Prog; while (p != nullptr) { if ( p->GetName() == name ) break; p = p->m_next; } if ( p == nullptr ) return false; p->GetPosition(start, stop, modestart, modestop); return true; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::Run(void* pUser, int timer) { bool ok; if (m_pStack == nullptr || m_pRun == nullptr) goto error; m_ErrorCode = 0; m_pStack->Reset(pUser); // empty the possible previous error, and resets the timer if ( timer >= 0 ) m_pStack->SetTimer(timer); m_pStack->SetBotCall(this); // bases for routines #if STACKRUN // resumes execution on the top of the stack ok = m_pStack->Execute(); if ( ok ) { // returns to normal execution ok = m_pRun->Execute(nullptr, m_pStack, m_pInstance); } #else ok = m_pRun->Execute(nullptr, m_pStack, m_pInstance); #endif // completed on a mistake? if (!ok && !m_pStack->IsOk()) { m_ErrorCode = m_pStack->GetError(m_ErrorStart, m_ErrorEnd); #if STACKMEM m_pStack->Delete(); #else delete m_pStack; #endif m_pStack = nullptr; return true; // execution is finished! } if ( ok ) m_pRun = nullptr; // more function in execution return ok; error: m_ErrorCode = CBotErrNoRun; return true; } //////////////////////////////////////////////////////////////////////////////// void CBotProgram::Stop() { #if STACKMEM m_pStack->Delete(); #else delete m_pStack; #endif m_pStack = nullptr; m_pRun = nullptr; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::GetRunPos(std::string& FunctionName, int& start, int& end) { FunctionName = nullptr; start = end = 0; if (m_pStack == nullptr) return false; m_pStack->GetRunPos(FunctionName, start, end); return true; } //////////////////////////////////////////////////////////////////////////////// CBotVar* CBotProgram::GetStackVars(std::string& FunctionName, int level) { FunctionName = nullptr; if (m_pStack == nullptr) return nullptr; return m_pStack->GetStackVars(FunctionName, level); } //////////////////////////////////////////////////////////////////////////////// void CBotProgram::SetTimer(int n) { CBotStack::SetTimer( n ); } //////////////////////////////////////////////////////////////////////////////// int CBotProgram::GetError() { return m_ErrorCode; } //////////////////////////////////////////////////////////////////////////////// void CBotProgram::SetIdent(long n) { m_Ident = n; } //////////////////////////////////////////////////////////////////////////////// long CBotProgram::GetIdent() { return m_Ident; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::GetError(int& code, int& start, int& end) { code = m_ErrorCode; start = m_ErrorStart; end = m_ErrorEnd; return code > 0; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::GetError(int& code, int& start, int& end, CBotProgram* &pProg) { code = m_ErrorCode; start = m_ErrorStart; end = m_ErrorEnd; pProg = this; return code > 0; } //////////////////////////////////////////////////////////////////////////////// std::string CBotProgram::GetErrorText(int code) { std::string TextError = LoadString(static_cast(code)); if (TextError.empty()) { char buf[100]; sprintf(buf, "Exception numéro %d.", code); TextError = buf; } return TextError; } //////////////////////////////////////////////////////////////////////////////// CBotFunction* CBotProgram::GetFunctions() { return m_Prog; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::AddFunction(const std::string& name, bool rExec(CBotVar* pVar, CBotVar* pResult, int& Exception, void* pUser), CBotTypResult rCompile(CBotVar*& pVar, void* pUser)) { // stores pointers to the two functions return CBotCall::AddFunction(name, rExec, rCompile); } //////////////////////////////////////////////////////////////////////////////// bool rSizeOf( CBotVar* pVar, CBotVar* pResult, int& ex, void* pUser ) { if ( pVar == nullptr ) return CBotErrLowParam; int i = 0; pVar = pVar->GetItemList(); while ( pVar != nullptr ) { i++; pVar = pVar->GetNext(); } pResult->SetValInt(i); return true; } //////////////////////////////////////////////////////////////////////////////// CBotTypResult cSizeOf( CBotVar* &pVar, void* pUser ) { if ( pVar == nullptr ) return CBotTypResult( CBotErrLowParam ); if ( pVar->GetType() != CBotTypArrayPointer ) return CBotTypResult( CBotErrBadParam ); return CBotTypResult( CBotTypInt ); } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::DefineNum(const std::string& name, long val) { return CBotToken::DefineNum(name, val); } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::SaveState(FILE* pf) { if (!WriteWord( pf, CBOTVERSION)) return false; if ( m_pStack != nullptr ) { if (!WriteWord( pf, 1)) return false; if (!WriteString( pf, m_pRun->GetName() )) return false; if (!m_pStack->SaveState(pf)) return false; } else { if (!WriteWord( pf, 0)) return false; } return true; } //////////////////////////////////////////////////////////////////////////////// bool CBotProgram::RestoreState(FILE* pf) { unsigned short w; std::string s; Stop(); if (!ReadWord( pf, w )) return false; if ( w != CBOTVERSION ) return false; if (!ReadWord( pf, w )) return false; if ( w == 0 ) return true; if (!ReadString( pf, s )) return false; Start(s); // point de reprise #if STACKMEM m_pStack->Delete(); #else delete m_pStack; #endif m_pStack = nullptr; // retrieves the stack from the memory // uses a nullptr pointer (m_pStack) but it's ok like that if (!m_pStack->RestoreState(pf, m_pStack)) return false; m_pStack->SetBotCall(this); // bases for routines // restored some states in the stack according to the structure m_pRun->RestoreState(nullptr, m_pStack, m_pInstance); return true; } //////////////////////////////////////////////////////////////////////////////// int CBotProgram::GetVersion() { return CBOTVERSION; } //////////////////////////////////////////////////////////////////////////////// void CBotProgram::Init() { CBotToken::DefineNum("CBotErrZeroDiv", CBotErrZeroDiv); // division by zero CBotToken::DefineNum("CBotErrNotInit", CBotErrNotInit); // uninitialized variable CBotToken::DefineNum("CBotErrBadThrow", CBotErrBadThrow); // throw a negative value CBotToken::DefineNum("CBotErrNoRetVal", CBotErrNoRetVal); // function did not return results CBotToken::DefineNum("CBotErrNoRun", CBotErrNoRun); // active Run () without a function // TODO: Is this actually a runtime error? CBotToken::DefineNum("CBotErrUndefFunc", CBotErrUndefFunc); // Calling a function that no longer exists CBotToken::DefineNum("CBotErrNotClass", CBotErrNotClass); // Class no longer exists CBotToken::DefineNum("CBotErrNull", CBotErrNull); // Attempted to use a null pointer CBotToken::DefineNum("CBotErrNan", CBotErrNan); // Can't do operations on nan CBotToken::DefineNum("CBotErrOutArray", CBotErrOutArray); // Attempted access out of bounds of an array CBotToken::DefineNum("CBotErrStackOver", CBotErrStackOver); // Stack overflow CBotToken::DefineNum("CBotErrDeletedPtr", CBotErrDeletedPtr); // Attempted to use deleted object CBotProgram::AddFunction("sizeof", rSizeOf, cSizeOf ); InitStringFunctions(); InitMathFunctions(); InitFileFunctions(); } //////////////////////////////////////////////////////////////////////////////// void CBotProgram::Free() { CBotToken::Free() ; CBotCall ::Free() ; CBotClass::Free() ; }