Fix and improve switch...case (#1008)

blender-script
melex750 2019-04-11 05:34:00 -04:00
parent 1058a326ad
commit 5f089f4a9b
22 changed files with 298 additions and 120 deletions

View File

@ -1798,6 +1798,9 @@ msgstr ""
msgid "Empty character constant"
msgstr ""
msgid "Duplicate label in switch"
msgstr ""
msgid "Dividing by zero"
msgstr ""

View File

@ -489,6 +489,9 @@ msgstr "Dolů (\\key gdown;)"
msgid "Drawer bot"
msgstr "Tužkobot"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Prach\\Prach a špína na robotech a budovách"

View File

@ -490,6 +490,9 @@ msgstr "Sinkt (\\key gdown;)"
msgid "Drawer bot"
msgstr "Zeichner"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Schmutz\\Schmutz auf Robotern und Bauten"

View File

@ -492,6 +492,9 @@ msgstr "Descend (\\key gdown;)"
msgid "Drawer bot"
msgstr "Robot dessinateur"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Salissures\\Salissures des robots et bâtiments"

View File

@ -488,6 +488,9 @@ msgstr "Dół (\\key gdown;)"
msgid "Drawer bot"
msgstr "Robot rysownik"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Kurz\\Kurz i bród na robotach i budynkach"

View File

@ -487,6 +487,9 @@ msgstr "Baixo (\\key gdown;)"
msgid "Drawer bot"
msgstr "Robô cartoonista"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Poeira\\Poeira e sujeira nos robôs e prédios"

View File

@ -496,6 +496,9 @@ msgstr "Вниз (\\key gdown;)"
msgid "Drawer bot"
msgstr "Рисовальщик"
msgid "Duplicate label in switch"
msgstr ""
msgid "Dust\\Dust and dirt on bots and buildings"
msgstr "Пыль\\Пыль и грязь на ботах и зданиях"

View File

@ -254,6 +254,7 @@ enum CBotError : int
CBotErrHexRange = 5053, //!< hex value out of range
CBotErrUnicodeName = 5054, //!< invalid universal character name
CBotErrCharEmpty = 5055, //!< empty character constant
CBotErrRedefCase = 5056, //!< duplicate label in switch
// Runtime errors
CBotErrZeroDiv = 6000, //!< division by zero

View File

@ -19,7 +19,7 @@
#include "CBot/CBotInstr/CBotCase.h"
#include "CBot/CBotInstr/CBotExprLitNum.h"
#include "CBot/CBotInstr/CBotTwoOpExpr.h"
#include "CBot/CBotStack.h"
#include "CBot/CBotCStack.h"
@ -30,69 +30,107 @@ namespace CBot
////////////////////////////////////////////////////////////////////////////////
CBotCase::CBotCase()
{
m_value = nullptr;
m_instr = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
CBotCase::~CBotCase()
{
delete m_value;
delete m_instr;
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack)
CBotInstr* CBotCase::Compile(CBotToken* &p, CBotCStack* pStack, std::unordered_map<long, CBotInstr*>& labels)
{
CBotCase* inst = new CBotCase(); // creates the object
CBotToken* pp = p; // preserves at the ^ token (starting position)
inst->SetToken(p);
if (!IsOfType(p, ID_CASE, ID_DEFAULT)) return nullptr; // should never happen
pStack->SetStartError(pp->GetStart());
if ( pp->GetType() == ID_CASE )
long labelValue = 0;
if (pp->GetType() == ID_CASE)
{
pp = p;
inst->m_value = CBot::CompileExprLitNum(p, pStack);
if (inst->m_value == nullptr )
CBotInstr* i = nullptr;
if (nullptr != (i = CBotTwoOpExpr::Compile(p, pStack, nullptr, true)))
{
pStack->SetError( CBotErrBadNum, pp );
delete inst;
return nullptr;
if (pStack->GetType() <= CBotTypLong)
{
CBotStack* pile = CBotStack::AllocateStack();
while ( !i->Execute(pile) );
labelValue = pile->GetVar()->GetValLong();
pile->Delete();
if (labels.count(labelValue) > 0)
{
pStack->SetError(CBotErrRedefCase, p->GetStart());
}
}
else
pStack->SetError(CBotErrBadNum, p->GetStart());
delete i;
}
}
if ( !IsOfType( p, ID_DOTS ))
{
pStack->SetError( CBotErrNoDoubleDots, p->GetStart() );
delete inst;
return nullptr;
else
pStack->SetError(CBotErrBadNum, p->GetStart());
}
return inst;
if (pStack->IsOk() && IsOfType(p, ID_DOTS))
{
CBotCase* newCase = new CBotCase();
newCase->SetToken(pp);
if (pp->GetType() == ID_CASE)
labels[labelValue] = newCase;
return newCase;
}
pStack->SetError(CBotErrNoDoubleDots, p->GetStart());
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
bool CBotCase::Execute(CBotStack* &pj)
{
return true; // the "case" statement does nothing!
if (m_instr == nullptr) return true;
CBotStack* pile = pj->AddStack(this, CBotStack::BlockVisibilityType::BLOCK);
int state = pile->GetState();
CBotInstr* p = m_instr;
while (state-- > 0) p = p->GetNext();
while (p != nullptr)
{
if (!p->Execute(pile)) return false;
pile->IncState();
p = p->GetNext();
}
pile->Delete();
return pj->IsOk();
}
////////////////////////////////////////////////////////////////////////////////
void CBotCase::RestoreState(CBotStack* &pj, bool bMain)
{
}
if (!bMain) return;
////////////////////////////////////////////////////////////////////////////////
bool CBotCase::CompCase(CBotStack* &pile, int val)
{
if (m_value == nullptr ) return true; // "default" case
CBotStack* pile = pj->RestoreStack(this);
if (pile == nullptr) return;
while (!m_value->Execute(pile)); // puts the value on the correspondent stack (without interruption)
return (pile->GetVal() == val); // compared with the given value
CBotInstr* p = m_instr;
int state = pile->GetState();
while (p != nullptr && state-- > 0)
{
p->RestoreState(pile, bMain);
p = p->GetNext();
}
if (p != nullptr) p->RestoreState(pile, bMain);
}
std::map<std::string, CBotInstr*> CBotCase::GetDebugLinks()
{
auto links = CBotInstr::GetDebugLinks();
links["m_value"] = m_value;
links["m_instr"] = m_instr;
return links;
}

View File

@ -21,6 +21,8 @@
#include "CBot/CBotInstr/CBotInstr.h"
#include <unordered_map>
namespace CBot
{
@ -42,7 +44,7 @@ public:
* \param pStack
* \return
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, std::unordered_map<long, CBotInstr*>& labels);
/*!
* \brief Execute Execution of instruction "case".
@ -58,22 +60,15 @@ public:
*/
void RestoreState(CBotStack* &pj, bool bMain) override;
/*!
* \brief CompCase Routine to find the entry point of "case" corresponding
* to the value seen.
* \param pj
* \param val
* \return
*/
bool CompCase(CBotStack* &pj, int val) override;
protected:
virtual const std::string GetDebugName() override { return "CBotCase"; }
virtual std::map<std::string, CBotInstr*> GetDebugLinks() override;
private:
//! Value to compare.
CBotInstr* m_value;
//! List of instructions after case label
CBotInstr* m_instr;
friend class CBotSwitch;
};
} // namespace CBot

View File

@ -40,8 +40,7 @@ CBotExprUnaire::~CBotExprUnaire()
delete m_expr;
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral)
CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral, bool bConstExpr)
{
int op = p->GetType();
CBotToken* pp = p;
@ -52,8 +51,10 @@ CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLite
CBotExprUnaire* inst = new CBotExprUnaire();
inst->SetToken(pp);
if (!bLiteral) inst->m_expr = CBotParExpr::Compile(p, pStk);
else inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk);
if (bConstExpr || !bLiteral)
inst->m_expr = CBotParExpr::Compile(p, pStk, bConstExpr);
else
inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk);
if (inst->m_expr != nullptr)
{

View File

@ -40,7 +40,7 @@ public:
* \param bLiteral If true, compiles only literal expressions Ex: ~11, -4.0, !false, not true
* \return The compiled instruction or nullptr
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral = false);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral = false, bool bConstExpr = false);
/*!
* \brief Execute

View File

@ -317,13 +317,6 @@ void CBotInstr::RestoreStateVar(CBotStack* &pile, bool bMain)
assert(0); // dad do not know, see the girls
}
////////////////////////////////////////////////////////////////////////////////
bool CBotInstr::CompCase(CBotStack* &pj, int val)
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, bool first)
{
if (IsOfType(p, ID_OPBRK))

View File

@ -191,17 +191,6 @@ public:
virtual void RestoreStateVar(CBotStack* &pile,
bool bMain);
/**
* \brief CompCase This routine is defined only for the subclass CBotCase
* this allows to make the call on all instructions CompCase to see if it's
* a case to the desired value..
* \param pj
* \param val
* \return
*/
virtual bool CompCase(CBotStack* &pj,
int val);
/**
* \brief SetToken Set the token corresponding to the instruction.
* \param p

View File

@ -32,6 +32,7 @@
#include "CBot/CBotInstr/CBotNew.h"
#include "CBot/CBotInstr/CBotPostIncExpr.h"
#include "CBot/CBotInstr/CBotPreIncExpr.h"
#include "CBot/CBotInstr/CBotTwoOpExpr.h"
#include "CBot/CBotVar/CBotVar.h"
@ -40,13 +41,15 @@
namespace CBot
{
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack)
CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack, bool bConstExpr)
{
CBotCStack* pStk = pStack->TokenStack();
pStk->SetStartError(p->GetStart());
if (bConstExpr)
return CBotParExpr::CompileConstExpr(p, pStack);
// is it an expression in parentheses?
if (IsOfType(p, ID_OPENPAR))
{
@ -224,4 +227,55 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack)
return pStack->Return(nullptr, pStk);
}
CBotInstr* CBotParExpr::CompileConstExpr(CBotToken* &p, CBotCStack* pStack)
{
CBotCStack* pStk = pStack->TokenStack();
// is it an expression in parentheses?
if (IsOfType(p, ID_OPENPAR))
{
CBotInstr* inst = CBotTwoOpExpr::Compile(p, pStk, nullptr, true);
if (nullptr != inst)
{
if (IsOfType(p, ID_CLOSEPAR))
{
return pStack->Return(inst, pStk);
}
pStk->SetError(CBotErrClosePar, p->GetStart());
}
delete inst;
return pStack->Return(nullptr, pStk);
}
// is this a unary operation?
CBotInstr* inst = CBotExprUnaire::Compile(p, pStk, true, true);
if (inst != nullptr || !pStk->IsOk())
return pStack->Return(inst, pStk);
// is it a number or DefineNum?
if (p->GetType() == TokenTypNum ||
p->GetType() == TokenTypDef )
{
CBotInstr* inst = CBot::CompileExprLitNum(p, pStk);
return pStack->Return(inst, pStk);
}
// is this a character?
if (p->GetType() == TokenTypChar)
{
CBotInstr* inst = CBotExprLitChar::Compile(p, pStk);
return pStack->Return(inst, pStk);
}
// is it sizeof operator ?
inst = CBot::CompileSizeOf(p, pStk);
if (inst != nullptr || !pStk->IsOk())
{
return pStack->Return(inst, pStk);
}
return pStack->Return(nullptr, pStk);
}
} // namespace CBot

View File

@ -52,7 +52,7 @@ public:
* \param pStack
* \return
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, bool bConstExpr = false);
/*!
* \brief Compile a literal expression ("string", number, true, false, null, nan, new)
@ -62,6 +62,8 @@ public:
*/
static CBotInstr* CompileLitExpr(CBotToken* &p, CBotCStack* pStack);
static CBotInstr* CompileConstExpr(CBotToken* &p, CBotCStack* pStack);
private:
CBotParExpr() = delete;
CBotParExpr(const CBotParExpr&) = delete;

View File

@ -57,7 +57,7 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
{
if ( nullptr != (inst->m_value = CBotExpression::Compile(p, pStk )) )
{
if ( pStk->GetType() < CBotTypLong )
if ( pStk->GetType() <= CBotTypLong )
{
if ( IsOfType(p, ID_CLOSEPAR ) )
{
@ -65,21 +65,35 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
{
IncLvl();
CBotCase* caseInst = nullptr;
CBotCStack* pStk2 = nullptr;
while( !IsOfType( p, ID_CLBLK ) )
{
if ( p->GetType() == ID_CASE || p->GetType() == ID_DEFAULT)
{
CBotCStack* pStk2 = pStk->TokenStack(p); // un petit bout de pile svp
delete pStk2;
pStk2 = pStk->TokenStack(p, true); // un petit bout de pile svp
CBotInstr* i = CBotCase::Compile( p, pStk2 );
if (i == nullptr)
caseInst = static_cast<CBotCase*>(CBotCase::Compile(p, pStk2, inst->m_labels));
if (caseInst == nullptr)
{
delete inst;
return pStack->Return(nullptr, pStk2);
}
delete pStk2;
if (inst->m_block == nullptr ) inst->m_block = i;
else inst->m_block->AddNext(i);
if (inst->m_block == nullptr ) inst->m_block = caseInst;
else inst->m_block->AddNext(caseInst);
if (ID_DEFAULT == caseInst->GetTokenType())
{
if (inst->m_default != nullptr)
{
pStk->SetError(CBotErrRedefCase, caseInst->GetToken());
delete inst;
return pStack->Return(nullptr, pStk);
}
inst->m_default = caseInst;
}
continue;
}
@ -90,13 +104,14 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
return pStack->Return(nullptr, pStk);
}
CBotInstr* i = CBotBlock::CompileBlkOrInst( p, pStk, true );
if ( !pStk->IsOk() )
CBotInstr* i = CBotBlock::CompileBlkOrInst(p, pStk2);
if ( !pStk2->IsOk() )
{
delete inst;
return pStack->Return(nullptr, pStk);
return pStack->Return(nullptr, pStk2);
}
inst->m_block->AddNext(i);
if (caseInst->m_instr == nullptr ) caseInst->m_instr = i;
else caseInst->m_instr->AddNext(i);
if ( p == nullptr )
{
@ -133,40 +148,21 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
bool CBotSwitch :: Execute(CBotStack* &pj)
{
CBotStack* pile1 = pj->AddStack(this); // adds an item to the stack
// if ( pile1 == EOX ) return true;
CBotInstr* p = m_block; // first expression
int state = pile1->GetState();
if (state == 0)
{
if ( !m_value->Execute(pile1) ) return false;
pile1->SetState(state = -1);
pile1->SetState(state = 1);
}
if ( pile1->IfStep() ) return false;
if ( state == -1 )
{
state = 0;
int val = pile1->GetVal(); // result of the value
auto it = m_labels.find(pile1->GetVar()->GetValLong());
CBotStack* pile2 = pile1->AddStack();
while ( p != nullptr ) // search for the corresponding case in a list
{
state++;
if ( p->CompCase( pile2, val ) ) break; // found the case
p = p->GetNext();
}
pile2->Delete();
CBotInstr* p = (it != m_labels.end()) ? it->second : m_default;
if ( p == nullptr ) return pj->Return(pile1); // completed if nothing
if ( !pile1->SetState(state) ) return false;
}
p = m_block; // returns to the beginning
while (state-->0) p = p->GetNext(); // advance in the list
while (--state > 0) p = p->GetNext();
while( p != nullptr )
{
@ -185,8 +181,6 @@ void CBotSwitch :: RestoreState(CBotStack* &pj, bool bMain)
CBotStack* pile1 = pj->RestoreStack(this); // adds an item to the stack
if ( pile1 == nullptr ) return;
CBotInstr* p = m_block; // first expression
int state = pile1->GetState();
if (state == 0)
{
@ -194,13 +188,11 @@ void CBotSwitch :: RestoreState(CBotStack* &pj, bool bMain)
return;
}
if ( state == -1 )
{
return;
}
auto it = m_labels.find(pile1->GetVar()->GetValLong());
// p = m_block; // returns to the beginning
while ( p != nullptr && state-- > 0 )
CBotInstr* p = (it != m_labels.end()) ? it->second : m_default;
while (p != nullptr && --state > 0)
{
p->RestoreState(pile1, false);
p = p->GetNext(); // advance in the list

View File

@ -21,6 +21,8 @@
#include "CBot/CBotInstr/CBotInstr.h"
#include <unordered_map>
namespace CBot
{
@ -64,8 +66,12 @@ protected:
private:
//! Value to seek
CBotInstr* m_value;
//! Instructions
CBotInstr* m_block;
//! List of case instructions
CBotInstr* m_block = nullptr;
//! Pointer to default label
CBotInstr* m_default = nullptr;
//! Map of case labels
std::unordered_map<long, CBotInstr*> m_labels;
};
} // namespace CBot

View File

@ -133,8 +133,7 @@ static bool TypeOk(int type, int test)
}
}
////////////////////////////////////////////////////////////////////////////////
CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations)
CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations, bool bConstExpr)
{
int typeMask;
@ -146,8 +145,8 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera
// search the intructions that may be suitable to the left of the operation
CBotInstr* left = (*pOp == 0) ?
CBotParExpr::Compile( p, pStk ) : // expression (...) left
CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B left
CBotParExpr::Compile(p, pStk, bConstExpr) : // expression (...) left
CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr); // expression A * B left
if (left == nullptr) return pStack->Return(nullptr, pStk); // if error, transmit
@ -158,7 +157,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera
CBotTypResult type1, type2;
type1 = pStk->GetTypResult(); // what kind of the first operand?
if (typeOp == ID_LOGIC) // special case provided for: ? op1: op2;
if (!bConstExpr && typeOp == ID_LOGIC) // special case provided for: ? op1: op2;
{
if ( !type1.Eq(CBotTypBoolean) )
{
@ -207,7 +206,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera
// looking statements that may be suitable for right
if ( nullptr != (inst->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp )) )
if ( nullptr != (inst->m_rightop = CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr)) )
// expression (...) right
{
// there is an second operand acceptable
@ -264,7 +263,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera
type1 = TypeRes;
p = p->GetNext(); // advance after
i->m_rightop = CBotTwoOpExpr::Compile( p, pStk, pOp );
i->m_rightop = CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr);
type2 = pStk->GetTypResult();
if ( !TypeCompatible (type1, type2, typeOp) ) // the results are compatible

View File

@ -65,7 +65,7 @@ public:
* \param pOperations
* \return
*/
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations = nullptr);
static CBotInstr* Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations = nullptr, bool bConstExpr = false);
/*!
* \brief Execute Performes the operation on two operands.

View File

@ -743,6 +743,7 @@ void InitializeRestext()
stringsCbot[CBot::CBotErrHexRange] = TR("Hex value out of range");
stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name");
stringsCbot[CBot::CBotErrCharEmpty] = TR("Empty character constant");
stringsCbot[CBot::CBotErrRedefCase] = TR("Duplicate label in switch");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized");

View File

@ -640,6 +640,92 @@ TEST_F(CBotUT, BinaryLiterals)
);
}
TEST_F(CBotUT, TestSwitchCase)
{
ExecuteTest(
"extern void Test_Switch_Case() {\n"
" int n = 0, c = 0;\n"
" for (int i = -9; i < 11; ++i) {\n"
" switch (i) {\n"
" case -9: n = -9; ++c; break;\n"
" case -8: n = -8; ++c; break;\n"
" case -7: n = -7; ++c; break;\n"
" case -6: n = -6; ++c; break;\n"
" case -5: n = -5; ++c; break;\n"
" case -4: n = -4; ++c; break;\n"
" case -3: n = -3; ++c; break;\n"
" case -2: n = -2; ++c; break;\n"
" case -1: n = -1; ++c; break;\n"
" case 0: n = 0; ++c; break;\n"
" case 1: n = 1; ++c; break;\n"
" case 2: n = 2; ++c; break;\n"
" case 3: n = 3; ++c; break;\n"
" case 4: n = 4; ++c; break;\n"
" case 5: n = 5; ++c; break;\n"
" case 6: n = 6; ++c; break;\n"
" case 7: n = 7; ++c; break;\n"
" case 8: n = 8; ++c; break;\n"
" case 9: n = 9; ++c; break;\n"
" default: n = 10; ++c; break;\n"
" }\n"
" ASSERT(n == i);\n"
" }\n"
" ASSERT(n == 10);\n"
" ASSERT(c == 20);\n"
"}\n"
"extern void Test_Case_With_Math() {\n"
" int n = 0, c = 0;\n"
" for (int i = -9; i < 11; ++i) {\n"
" switch (i * 10) {\n"
" case -9*10: n = -90; ++c; break;\n"
" case -8*10: n = -80; ++c; break;\n"
" case -7*10: n = -70; ++c; break;\n"
" case -6*10: n = -60; ++c; break;\n"
" case -5*10: n = -50; ++c; break;\n"
" case -4*10: n = -40; ++c; break;\n"
" case -3*10: n = -30; ++c; break;\n"
" case -2*10: n = -20; ++c; break;\n"
" case -1*10: n = -10; ++c; break;\n"
" case 0*10: n = 0; ++c; break;\n"
" case 1*10: n = 10; ++c; break;\n"
" case 2*10: n = 20; ++c; break;\n"
" case 3*10: n = 30; ++c; break;\n"
" case 4*10: n = 40; ++c; break;\n"
" case 5*10: n = 50; ++c; break;\n"
" case 6*10: n = 60; ++c; break;\n"
" case 7*10: n = 70; ++c; break;\n"
" case 8*10: n = 80; ++c; break;\n"
" case 9*10: n = 90; ++c; break;\n"
" default: n = 100; ++c; break;\n"
" }\n"
" ASSERT(n == i * 10);\n"
" }\n"
" ASSERT(n == 100);\n"
" ASSERT(c == 20);\n"
"}\n"
);
ExecuteTest(
"extern void Duplicate_Case() {\n"
" switch(0) {\n"
" case 1000:\n"
" case 10*100:\n"
" }\n"
"}\n",
CBotErrRedefCase
);
ExecuteTest(
"extern void Duplicate_Default() {\n"
" switch(0) {\n"
" default:\n"
" default:\n"
" }\n"
"}\n",
CBotErrRedefCase
);
}
TEST_F(CBotUT, ToString)
{
ExecuteTest(