Merge pull request #1439 from melex750/dev-cbot-repeat
Restore repeat(n) instruction in CBOTfix-squashed-planets
commit
3ab153225a
|
@ -100,6 +100,7 @@ enum TokenId
|
|||
ID_STATIC,
|
||||
ID_PROTECTED,
|
||||
ID_PRIVATE,
|
||||
ID_REPEAT,
|
||||
ID_INT,
|
||||
ID_FLOAT,
|
||||
ID_BOOLEAN,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<std::string, CBotInstr*> CBotRepeat::GetDebugLinks()
|
||||
{
|
||||
auto links = CBotInstr::GetDebugLinks();
|
||||
links["m_expr"] = m_expr;
|
||||
links["m_block"] = m_block;
|
||||
return links;
|
||||
}
|
||||
|
||||
} // namespace CBot
|
|
@ -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<std::string, CBotInstr*> 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
|
|
@ -65,6 +65,7 @@ static const boost::bimap<TokenId, std::string> KEYWORDS = makeBimap<TokenId, st
|
|||
{ID_STATIC, "static"},
|
||||
{ID_PROTECTED, "protected"},
|
||||
{ID_PRIVATE, "private"},
|
||||
{ID_REPEAT, "repeat"},
|
||||
{ID_INT, "int"},
|
||||
{ID_FLOAT, "float"},
|
||||
{ID_BOOLEAN, "boolean"},
|
||||
|
|
|
@ -100,6 +100,8 @@ set(SOURCES
|
|||
CBotInstr/CBotPostIncExpr.h
|
||||
CBotInstr/CBotPreIncExpr.cpp
|
||||
CBotInstr/CBotPreIncExpr.h
|
||||
CBotInstr/CBotRepeat.cpp
|
||||
CBotInstr/CBotRepeat.h
|
||||
CBotInstr/CBotReturn.cpp
|
||||
CBotInstr/CBotReturn.h
|
||||
CBotInstr/CBotSwitch.cpp
|
||||
|
|
|
@ -261,6 +261,7 @@ std::string GetHelpFilename(const char *token)
|
|||
|
||||
if ( strcmp(token, "if" ) == 0 ) helpfile = "cbot/if";
|
||||
if ( strcmp(token, "else" ) == 0 ) helpfile = "cbot/if";
|
||||
if ( strcmp(token, "repeat" ) == 0 ) helpfile = "cbot/repeat";
|
||||
if ( strcmp(token, "for" ) == 0 ) helpfile = "cbot/for";
|
||||
if ( strcmp(token, "while" ) == 0 ) helpfile = "cbot/while";
|
||||
if ( strcmp(token, "do" ) == 0 ) helpfile = "cbot/do";
|
||||
|
@ -543,6 +544,7 @@ const char* GetHelpText(const char *token)
|
|||
{
|
||||
if ( strcmp(token, "if" ) == 0 ) return "if ( condition ) { code }";
|
||||
if ( strcmp(token, "else" ) == 0 ) return "else { code }";
|
||||
if ( strcmp(token, "repeat" ) == 0 ) return "repeat ( number )";
|
||||
if ( strcmp(token, "for" ) == 0 ) return "for ( before ; condition ; end )";
|
||||
if ( strcmp(token, "while" ) == 0 ) return "while ( condition ) { code }";
|
||||
if ( strcmp(token, "do" ) == 0 ) return "do { code } while ( condition );";
|
||||
|
|
|
@ -726,6 +726,71 @@ TEST_F(CBotUT, TestSwitchCase)
|
|||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, TestRepeatInstruction)
|
||||
{
|
||||
ExecuteTest(
|
||||
"extern void TestRepeat() {\n"
|
||||
" int c = 0;\n"
|
||||
" for (int i = 1; i < 11; ++i)\n"
|
||||
" {\n"
|
||||
" repeat (i) ++c;\n"
|
||||
" }\n"
|
||||
" ASSERT(c == 55);\n"
|
||||
"}\n"
|
||||
"extern void TestRepeatBreakAndContinue() {\n"
|
||||
" int c = 0;\n"
|
||||
" repeat (10)\n"
|
||||
" {\n"
|
||||
" if (++c == 5) break;\n"
|
||||
" continue;\n"
|
||||
" FAIL();\n"
|
||||
" }\n"
|
||||
" ASSERT(c == 5);\n"
|
||||
" label:repeat (10)\n"
|
||||
" {\n"
|
||||
" if (++c == 10) break label;\n"
|
||||
" continue label;\n"
|
||||
" FAIL();\n"
|
||||
" }\n"
|
||||
" ASSERT(c == 10);\n"
|
||||
"}\n"
|
||||
"extern void NoRepeatNumberLessThanOne() {\n"
|
||||
" repeat (0) FAIL();\n"
|
||||
" repeat (-1) FAIL();\n"
|
||||
" repeat (-2) FAIL();\n"
|
||||
"}\n"
|
||||
"extern void EvaluateExpressionOnlyOnce() {\n"
|
||||
" int c = 0;\n"
|
||||
" repeat (c + 5) ASSERT(++c < 6);\n"
|
||||
" ASSERT(c == 5);\n"
|
||||
"}\n"
|
||||
);
|
||||
ExecuteTest(
|
||||
"extern void MissingOpenParen() {\n"
|
||||
" repeat ;\n"
|
||||
"}\n",
|
||||
CBotErrOpenPar
|
||||
);
|
||||
ExecuteTest(
|
||||
"extern void MissingNumber() {\n"
|
||||
" repeat (;\n"
|
||||
"}\n",
|
||||
CBotErrBadNum
|
||||
);
|
||||
ExecuteTest(
|
||||
"extern void WrongType() {\n"
|
||||
" repeat (\"not number\");\n"
|
||||
"}\n",
|
||||
CBotErrBadType1
|
||||
);
|
||||
ExecuteTest(
|
||||
"extern void MissingCloseParen() {\n"
|
||||
" repeat (2;\n"
|
||||
"}\n",
|
||||
CBotErrClosePar
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(CBotUT, ToString)
|
||||
{
|
||||
ExecuteTest(
|
||||
|
|
Loading…
Reference in New Issue