diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 9ed38000..963ca261 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -100,6 +100,7 @@ enum TokenId ID_STATIC, ID_PROTECTED, ID_PRIVATE, + ID_REPEAT, ID_INT, ID_FLOAT, ID_BOOLEAN, diff --git a/src/CBot/CBotInstr/CBotInstr.cpp b/src/CBot/CBotInstr/CBotInstr.cpp index b6fbbc66..0115ba28 100644 --- a/src/CBot/CBotInstr/CBotInstr.cpp +++ b/src/CBot/CBotInstr/CBotInstr.cpp @@ -30,6 +30,7 @@ #include "CBot/CBotInstr/CBotExpression.h" #include "CBot/CBotInstr/CBotFor.h" #include "CBot/CBotInstr/CBotIf.h" +#include "CBot/CBotInstr/CBotRepeat.h" #include "CBot/CBotInstr/CBotReturn.h" #include "CBot/CBotInstr/CBotSwitch.h" #include "CBot/CBotInstr/CBotThrow.h" @@ -176,7 +177,7 @@ CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) { type = pp->GetType(); // Allow only instructions that accept a label - if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, 0)) + if (!IsOfTypeList(pp, ID_WHILE, ID_FOR, ID_DO, ID_REPEAT, 0)) { pStack->SetError(CBotErrLabel, pp->GetStart()); return nullptr; @@ -195,6 +196,9 @@ CBotInstr* CBotInstr::Compile(CBotToken* &p, CBotCStack* pStack) case ID_DO: return CBotDo::Compile(p, pStack); + case ID_REPEAT: + return CBotRepeat::Compile(p, pStack); + case ID_BREAK: case ID_CONTINUE: return CBotBreak::Compile(p, pStack); diff --git a/src/CBot/CBotInstr/CBotRepeat.cpp b/src/CBot/CBotInstr/CBotRepeat.cpp new file mode 100644 index 00000000..8d8e0553 --- /dev/null +++ b/src/CBot/CBotInstr/CBotRepeat.cpp @@ -0,0 +1,165 @@ +/* + * 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/CBotInstr/CBotRepeat.h" + +#include "CBot/CBotInstr/CBotBlock.h" +#include "CBot/CBotInstr/CBotExpression.h" + +#include "CBot/CBotCStack.h" +#include "CBot/CBotStack.h" + +namespace CBot +{ + +CBotRepeat::CBotRepeat() +{ + m_expr = nullptr; + m_block = nullptr; +} + +CBotRepeat::~CBotRepeat() +{ + delete m_expr; + delete m_block; +} + +CBotInstr* CBotRepeat::Compile(CBotToken* &p, CBotCStack* pStack) +{ + CBotRepeat* inst = new CBotRepeat(); // creates the object + CBotToken* pp = p; // preserves at the ^ token (starting position) + + if ( IsOfType( p, TokenTypVar ) && IsOfType( p, ID_DOTS ) ) + inst->m_label = pp->GetString(); // register the name of label + + inst->SetToken(p); + if (!IsOfType(p, ID_REPEAT)) return nullptr; // should never happen + + CBotCStack* pStk = pStack->TokenStack(pp); + + if ( IsOfType(p, ID_OPENPAR ) ) + { + CBotToken* ppp = p; // preserves the ^ token (starting position) + if ( nullptr != (inst->m_expr = CBotExpression::Compile( p, pStk )) ) + { + if ( pStk->GetType() < CBotTypLong ) + { + if ( IsOfType(p, ID_CLOSEPAR ) ) + { + IncLvl(inst->m_label); + inst->m_block = CBotBlock::CompileBlkOrInst( p, pStk, true ); + DecLvl(); + + if ( pStk->IsOk() ) // the statement block is ok (it may be empty!) + return pStack->Return(inst, pStk); + } + pStack->SetError(CBotErrClosePar, p->GetStart()); + } + pStk->SetStartError(ppp->GetStart()); + pStk->SetError(CBotErrBadType1, p->GetStart()); + } + pStack->SetError(CBotErrBadNum, p); + } + pStack->SetError(CBotErrOpenPar, p->GetStart()); + + delete inst; + return pStack->Return(nullptr, pStk); +} + +// execution of intruction "repeat" + +bool CBotRepeat::Execute(CBotStack* &pj) +{ + CBotStack* pile = pj->AddStack(this); // adds an item to the stack + // or find in case of recovery + if ( pile->IfStep() ) return false; + + while( true ) switch( pile->GetState() ) // executes the loop + { // there are two possible states (depending on recovery) + case 0: + // evaluates the number of iterations + if ( !m_expr->Execute(pile) ) return false; // interrupted here ? + + // the result of the condition is on the stack + + // terminates if an error or if the condition is false + int n; + if ( !pile->IsOk() || ( n = pile->GetVal() ) < 1 ) + return pj->Return(pile); // releases the stack + + // puts the number of iterations +1 to the "state" + + if (!pile->SetState(n+1)) return false; // ready for further + continue; // continue as a result + + case 1: + // normal end of the loop + return pj->Return(pile); // releases the stack + + default: + // evaluates the associated statement block + if ( m_block != nullptr && !m_block->Execute(pile) ) + { + if (pile->IfContinue(pile->GetState()-1, m_label)) continue; // if continued, will return to test + return pj->BreakReturn(pile, m_label); // releases the stack + } + + // terminates if there is an error + if (!pile->IsOk()) return pj->Return(pile); // releases the stack + + // returns to the test again + if (!pile->SetState(pile->GetState()-1, 0)) return false; + continue; + } +} + +void CBotRepeat::RestoreState(CBotStack* &pj, bool bMain) +{ + if ( !bMain ) return; + CBotStack* pile = pj->RestoreStack(this); // adds an item to the stack + if ( pile == nullptr ) return; + + switch( pile->GetState() ) + { // there are two possible states (depending on recovery) + case 0: + // evaluates the condition + m_expr->RestoreState(pile, bMain); + return; + + case 1: + // evaluates the associated statement block + if ( m_block != nullptr ) m_block->RestoreState(pile, bMain); + return; + } +} + +std::string CBotRepeat::GetDebugData() +{ + return !m_label.empty() ? "m_label = " + m_label : ""; +} + +std::map CBotRepeat::GetDebugLinks() +{ + auto links = CBotInstr::GetDebugLinks(); + links["m_expr"] = m_expr; + links["m_block"] = m_block; + return links; +} + +} // namespace CBot diff --git a/src/CBot/CBotInstr/CBotRepeat.h b/src/CBot/CBotInstr/CBotRepeat.h new file mode 100644 index 00000000..d60f4a09 --- /dev/null +++ b/src/CBot/CBotInstr/CBotRepeat.h @@ -0,0 +1,62 @@ +/* + * 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 + */ + +#pragma once + +#include "CBot/CBotInstr/CBotInstr.h" + +namespace CBot +{ + +/** + * \brief The "repeat" loop - repeat (times) { ... } + */ +class CBotRepeat : public CBotInstr +{ +public: + CBotRepeat(); + ~CBotRepeat(); + + /// Static method used for compilation + static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack); + + /// Execute + bool Execute(CBotStack* &pj) override; + + /// Restore state + void RestoreState(CBotStack* &pj, bool bMain) override; + +protected: + virtual const std::string GetDebugName() override { return "CBotRepeat"; } + virtual std::string GetDebugData() override; + virtual std::map GetDebugLinks() override; + +private: + /// Number of iterations + CBotInstr* m_expr; + + /// Instructions + CBotInstr* m_block; + + /// Label + std::string m_label; // a label if there is + +}; + +} // namespace CBot diff --git a/src/CBot/CBotToken.cpp b/src/CBot/CBotToken.cpp index 2b98b582..38402d94 100644 --- a/src/CBot/CBotToken.cpp +++ b/src/CBot/CBotToken.cpp @@ -65,6 +65,7 @@ static const boost::bimap KEYWORDS = makeBimap