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" msgid "Empty character constant"
msgstr "" msgstr ""
msgid "Duplicate label in switch"
msgstr ""
msgid "Dividing by zero" msgid "Dividing by zero"
msgstr "" msgstr ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,8 @@
#include "CBot/CBotInstr/CBotInstr.h" #include "CBot/CBotInstr/CBotInstr.h"
#include <unordered_map>
namespace CBot namespace CBot
{ {
@ -42,7 +44,7 @@ public:
* \param pStack * \param pStack
* \return * \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". * \brief Execute Execution of instruction "case".
@ -58,22 +60,15 @@ public:
*/ */
void RestoreState(CBotStack* &pj, bool bMain) override; 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: protected:
virtual const std::string GetDebugName() override { return "CBotCase"; } virtual const std::string GetDebugName() override { return "CBotCase"; }
virtual std::map<std::string, CBotInstr*> GetDebugLinks() override; virtual std::map<std::string, CBotInstr*> GetDebugLinks() override;
private: private:
//! Value to compare. //! List of instructions after case label
CBotInstr* m_value; CBotInstr* m_instr;
friend class CBotSwitch;
}; };
} // namespace CBot } // namespace CBot

View File

@ -40,8 +40,7 @@ CBotExprUnaire::~CBotExprUnaire()
delete m_expr; delete m_expr;
} }
//////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral, bool bConstExpr)
CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLiteral)
{ {
int op = p->GetType(); int op = p->GetType();
CBotToken* pp = p; CBotToken* pp = p;
@ -52,8 +51,10 @@ CBotInstr* CBotExprUnaire::Compile(CBotToken* &p, CBotCStack* pStack, bool bLite
CBotExprUnaire* inst = new CBotExprUnaire(); CBotExprUnaire* inst = new CBotExprUnaire();
inst->SetToken(pp); inst->SetToken(pp);
if (!bLiteral) inst->m_expr = CBotParExpr::Compile(p, pStk); if (bConstExpr || !bLiteral)
else inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk); inst->m_expr = CBotParExpr::Compile(p, pStk, bConstExpr);
else
inst->m_expr = CBotParExpr::CompileLitExpr(p, pStk);
if (inst->m_expr != nullptr) 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 * \param bLiteral If true, compiles only literal expressions Ex: ~11, -4.0, !false, not true
* \return The compiled instruction or nullptr * \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 * \brief Execute

View File

@ -317,13 +317,6 @@ void CBotInstr::RestoreStateVar(CBotStack* &pile, bool bMain)
assert(0); // dad do not know, see the girls 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) CBotInstr* CBotInstr::CompileArray(CBotToken* &p, CBotCStack* pStack, CBotTypResult type, bool first)
{ {
if (IsOfType(p, ID_OPBRK)) if (IsOfType(p, ID_OPBRK))

View File

@ -191,17 +191,6 @@ public:
virtual void RestoreStateVar(CBotStack* &pile, virtual void RestoreStateVar(CBotStack* &pile,
bool bMain); 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. * \brief SetToken Set the token corresponding to the instruction.
* \param p * \param p

View File

@ -32,6 +32,7 @@
#include "CBot/CBotInstr/CBotNew.h" #include "CBot/CBotInstr/CBotNew.h"
#include "CBot/CBotInstr/CBotPostIncExpr.h" #include "CBot/CBotInstr/CBotPostIncExpr.h"
#include "CBot/CBotInstr/CBotPreIncExpr.h" #include "CBot/CBotInstr/CBotPreIncExpr.h"
#include "CBot/CBotInstr/CBotTwoOpExpr.h"
#include "CBot/CBotVar/CBotVar.h" #include "CBot/CBotVar/CBotVar.h"
@ -40,13 +41,15 @@
namespace CBot namespace CBot
{ {
//////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack, bool bConstExpr)
CBotInstr* CBotParExpr::Compile(CBotToken* &p, CBotCStack* pStack)
{ {
CBotCStack* pStk = pStack->TokenStack(); CBotCStack* pStk = pStack->TokenStack();
pStk->SetStartError(p->GetStart()); pStk->SetStartError(p->GetStart());
if (bConstExpr)
return CBotParExpr::CompileConstExpr(p, pStack);
// is it an expression in parentheses? // is it an expression in parentheses?
if (IsOfType(p, ID_OPENPAR)) if (IsOfType(p, ID_OPENPAR))
{ {
@ -224,4 +227,55 @@ CBotInstr* CBotParExpr::CompileLitExpr(CBotToken* &p, CBotCStack* pStack)
return pStack->Return(nullptr, pStk); 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 } // namespace CBot

View File

@ -52,7 +52,7 @@ public:
* \param pStack * \param pStack
* \return * \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) * \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* CompileLitExpr(CBotToken* &p, CBotCStack* pStack);
static CBotInstr* CompileConstExpr(CBotToken* &p, CBotCStack* pStack);
private: private:
CBotParExpr() = delete; CBotParExpr() = delete;
CBotParExpr(const 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 ( nullptr != (inst->m_value = CBotExpression::Compile(p, pStk )) )
{ {
if ( pStk->GetType() < CBotTypLong ) if ( pStk->GetType() <= CBotTypLong )
{ {
if ( IsOfType(p, ID_CLOSEPAR ) ) if ( IsOfType(p, ID_CLOSEPAR ) )
{ {
@ -65,21 +65,35 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
{ {
IncLvl(); IncLvl();
CBotCase* caseInst = nullptr;
CBotCStack* pStk2 = nullptr;
while( !IsOfType( p, ID_CLBLK ) ) while( !IsOfType( p, ID_CLBLK ) )
{ {
if ( p->GetType() == ID_CASE || p->GetType() == ID_DEFAULT) 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 ); caseInst = static_cast<CBotCase*>(CBotCase::Compile(p, pStk2, inst->m_labels));
if (i == nullptr) if (caseInst == nullptr)
{ {
delete inst; delete inst;
return pStack->Return(nullptr, pStk2); return pStack->Return(nullptr, pStk2);
} }
delete pStk2;
if (inst->m_block == nullptr ) inst->m_block = i; if (inst->m_block == nullptr ) inst->m_block = caseInst;
else inst->m_block->AddNext(i); 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; continue;
} }
@ -90,13 +104,14 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
return pStack->Return(nullptr, pStk); return pStack->Return(nullptr, pStk);
} }
CBotInstr* i = CBotBlock::CompileBlkOrInst( p, pStk, true ); CBotInstr* i = CBotBlock::CompileBlkOrInst(p, pStk2);
if ( !pStk->IsOk() ) if ( !pStk2->IsOk() )
{ {
delete inst; 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 ) if ( p == nullptr )
{ {
@ -133,40 +148,21 @@ CBotInstr* CBotSwitch::Compile(CBotToken* &p, CBotCStack* pStack)
bool CBotSwitch :: Execute(CBotStack* &pj) bool CBotSwitch :: Execute(CBotStack* &pj)
{ {
CBotStack* pile1 = pj->AddStack(this); // adds an item to the stack 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(); int state = pile1->GetState();
if (state == 0) if (state == 0)
{ {
if ( !m_value->Execute(pile1) ) return false; if ( !m_value->Execute(pile1) ) return false;
pile1->SetState(state = -1); pile1->SetState(state = 1);
} }
if ( pile1->IfStep() ) return false; if ( pile1->IfStep() ) return false;
if ( state == -1 ) auto it = m_labels.find(pile1->GetVar()->GetValLong());
{
state = 0;
int val = pile1->GetVal(); // result of the value
CBotStack* pile2 = pile1->AddStack(); CBotInstr* p = (it != m_labels.end()) ? it->second : m_default;
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();
if ( p == nullptr ) return pj->Return(pile1); // completed if nothing while (--state > 0) p = p->GetNext();
if ( !pile1->SetState(state) ) return false;
}
p = m_block; // returns to the beginning
while (state-->0) p = p->GetNext(); // advance in the list
while( p != nullptr ) 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 CBotStack* pile1 = pj->RestoreStack(this); // adds an item to the stack
if ( pile1 == nullptr ) return; if ( pile1 == nullptr ) return;
CBotInstr* p = m_block; // first expression
int state = pile1->GetState(); int state = pile1->GetState();
if (state == 0) if (state == 0)
{ {
@ -194,13 +188,11 @@ void CBotSwitch :: RestoreState(CBotStack* &pj, bool bMain)
return; return;
} }
if ( state == -1 ) auto it = m_labels.find(pile1->GetVar()->GetValLong());
{
return;
}
// p = m_block; // returns to the beginning CBotInstr* p = (it != m_labels.end()) ? it->second : m_default;
while ( p != nullptr && state-- > 0 )
while (p != nullptr && --state > 0)
{ {
p->RestoreState(pile1, false); p->RestoreState(pile1, false);
p = p->GetNext(); // advance in the list p = p->GetNext(); // advance in the list

View File

@ -21,6 +21,8 @@
#include "CBot/CBotInstr/CBotInstr.h" #include "CBot/CBotInstr/CBotInstr.h"
#include <unordered_map>
namespace CBot namespace CBot
{ {
@ -64,8 +66,12 @@ protected:
private: private:
//! Value to seek //! Value to seek
CBotInstr* m_value; CBotInstr* m_value;
//! Instructions //! List of case instructions
CBotInstr* m_block; 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 } // namespace CBot

View File

@ -133,8 +133,7 @@ static bool TypeOk(int type, int test)
} }
} }
//////////////////////////////////////////////////////////////////////////////// CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations, bool bConstExpr)
CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOperations)
{ {
int typeMask; 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 // search the intructions that may be suitable to the left of the operation
CBotInstr* left = (*pOp == 0) ? CBotInstr* left = (*pOp == 0) ?
CBotParExpr::Compile( p, pStk ) : // expression (...) left CBotParExpr::Compile(p, pStk, bConstExpr) : // expression (...) left
CBotTwoOpExpr::Compile( p, pStk, pOp ); // expression A * B left CBotTwoOpExpr::Compile(p, pStk, pOp, bConstExpr); // expression A * B left
if (left == nullptr) return pStack->Return(nullptr, pStk); // if error, transmit 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; CBotTypResult type1, type2;
type1 = pStk->GetTypResult(); // what kind of the first operand? 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) ) 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 // 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 // expression (...) right
{ {
// there is an second operand acceptable // there is an second operand acceptable
@ -264,7 +263,7 @@ CBotInstr* CBotTwoOpExpr::Compile(CBotToken* &p, CBotCStack* pStack, int* pOpera
type1 = TypeRes; type1 = TypeRes;
p = p->GetNext(); // advance after 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(); type2 = pStk->GetTypResult();
if ( !TypeCompatible (type1, type2, typeOp) ) // the results are compatible if ( !TypeCompatible (type1, type2, typeOp) ) // the results are compatible

View File

@ -65,7 +65,7 @@ public:
* \param pOperations * \param pOperations
* \return * \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. * \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::CBotErrHexRange] = TR("Hex value out of range");
stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name"); stringsCbot[CBot::CBotErrUnicodeName] = TR("Invalid universal character name");
stringsCbot[CBot::CBotErrCharEmpty] = TR("Empty character constant"); stringsCbot[CBot::CBotErrCharEmpty] = TR("Empty character constant");
stringsCbot[CBot::CBotErrRedefCase] = TR("Duplicate label in switch");
stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero");
stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); 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) TEST_F(CBotUT, ToString)
{ {
ExecuteTest( ExecuteTest(