diff options
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | include/parsingtoolbox.h | 40 | ||||
| -rw-r--r-- | node/switchcasenode.cpp | 146 | ||||
| -rw-r--r-- | node/switchcasenode.h | 54 | ||||
| -rw-r--r-- | parsingtoolbox.cpp | 45 | ||||
| -rw-r--r-- | tests/dice/tst_dice.cpp | 86 |
6 files changed, 354 insertions, 20 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c1eec8d..9afc512 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ SET( dice_sources ${CMAKE_CURRENT_SOURCE_DIR}/node/variablenode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/valueslistnode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/node/repeaternode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/switchcasenode.cpp ) IF(STATIC_BUILD) @@ -65,7 +66,7 @@ IF(STATIC_BUILD) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() -set(documentation README.md HelpMe.md) +set(documentation README.md HelpMe.md CHANGELOG) add_library(diceparser_shared SHARED ${dice_sources} ${documentation}) diff --git a/include/parsingtoolbox.h b/include/parsingtoolbox.h index 1817704..d83e063 100644 --- a/include/parsingtoolbox.h +++ b/include/parsingtoolbox.h @@ -40,6 +40,7 @@ class RepeaterNode; class DiceAlias; class ExplodeDiceNode; +class SwitchCaseNode; class SubtituteInfo { @@ -89,24 +90,25 @@ public: }; enum OptionOperator { - KeepAndExplode, - Keep, - Reroll, - RerollUntil, - Explode, - Sort, - Count, - RerollAndAdd, - Merge, - ifOperator, - Painter, - Filter, - Split, - Group, - Occurences, - Unique, - Bind, - AllSameExplode + KeepAndExplode, // K + Keep, // k + Reroll, // r + RerollUntil, // R + Explode, // e + Sort, // s + Count, // c + RerollAndAdd, // a + Merge, // m + ifOperator, // i + Painter, // p + Filter, // f + Split, // y + Group, // g + Occurences, // o + Unique, // u + Bind, // b + AllSameExplode, // t + SwitchCaseOption // S }; enum DiceOperator { @@ -131,6 +133,7 @@ public: // parsing tools static bool readAscending(QString& str); + static bool readStopAtFirst(QString& str); bool readLogicOperator(QString& str, BooleanCondition::LogicOperator& op); Validator* readValidator(QString& str, bool hasSquare= false); ValidatorList* readValidatorList(QString& str); @@ -181,6 +184,7 @@ public: bool readBlocInstruction(QString& str, ExecutionNode*& resultnode); bool readOption(QString&, ExecutionNode* node); // OptionOperator& option, bool readValuesList(QString& str, ExecutionNode*& node); + bool readSwitchCaseNode(QString& str, SwitchCaseNode* node); // Error bool hasError() const; diff --git a/node/switchcasenode.cpp b/node/switchcasenode.cpp new file mode 100644 index 0000000..99450d2 --- /dev/null +++ b/node/switchcasenode.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2021 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This software 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "switchcasenode.h" + +#include "parsingtoolbox.h" +#include "stringresult.h" + +SwitchCaseNode::SwitchCaseNode() : m_stringResult(new StringResult) +{ + m_result= m_stringResult; +} + +void SwitchCaseNode::setStopAtFirt(bool b) +{ + m_stopAtFirst= b; +} + +void SwitchCaseNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + m_errors.insert(Dice::ERROR_CODE::NO_PREVIOUS_ERROR, + QStringLiteral("No previous node before Swith/Case operator")); + return; + } + auto previousResult= previous->getResult(); + m_result->setPrevious(previousResult); + + if(nullptr == previousResult + || (!previousResult->hasResultOfType(Dice::RESULT_TYPE::SCALAR) + && !previousResult->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST))) + { + m_errors.insert(Dice::ERROR_CODE::NO_VALID_RESULT, + QStringLiteral("No scalar or dice result before Swith/Case operator")); + return; + } + + QList<Die*> dieList; + if(previousResult->hasResultOfType(Dice::RESULT_TYPE::SCALAR)) + { + auto resultScalar= previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + auto d= new Die(); + d->insertRollValue(resultScalar); + dieList.append(d); + } + else + { + auto diceResult= dynamic_cast<DiceResult*>(previousResult); + if(diceResult) + dieList.append(diceResult->getResultList()); + } + + for(auto die : dieList) + { + for(auto const& info : qAsConst(m_branchList)) + { + if(info->validatorList) + { + auto res= info->validatorList->hasValid(die, true, true); + if(!res) + continue; + } + + info->node->run(m_previousNode); + auto lastNode= ParsingToolBox::getLeafNode(info->node); + if(lastNode && lastNode->getResult()) + { + m_stringResult->addText(lastNode->getResult()->getStringResult()); + } + else + m_stringResult->addText(QString()); + + if(m_stopAtFirst) + break; + } + } + + if(m_stringResult->getText().isEmpty()) + m_errors.insert(Dice::ERROR_CODE::NO_VALID_RESULT, QStringLiteral("No value fits the Swith/Case operator")); + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString SwitchCaseNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"SwitchCaseNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 SwitchCaseNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* SwitchCaseNode::getCopy() const +{ + SwitchCaseNode* node= new SwitchCaseNode(); + for(auto const& info : qAsConst(m_branchList)) + { + node->insertCase(info->node, info->validatorList); + } + + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +void SwitchCaseNode::insertCase(ExecutionNode* node, ValidatorList* validator) +{ + std::unique_ptr<CaseInfo> info(new CaseInfo{validator, node}); + m_branchList.push_back(std::move(info)); +} diff --git a/node/switchcasenode.h b/node/switchcasenode.h new file mode 100644 index 0000000..719da90 --- /dev/null +++ b/node/switchcasenode.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2021 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This software 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef SWITCHCASENODE_H +#define SWITCHCASENODE_H + +#include <memory> + +#include "executionnode.h" +#include "validatorlist.h" + +struct CaseInfo +{ + ValidatorList* validatorList; + ExecutionNode* node; +}; +class StringResult; +class SwitchCaseNode : public ExecutionNode +{ +public: + SwitchCaseNode(); + void setStopAtFirt(bool b); + + void run(ExecutionNode* previous= nullptr) override; + + QString toString(bool withLabel) const override; + qint64 getPriority() const override; + ExecutionNode* getCopy() const override; + + void insertCase(ExecutionNode* node, ValidatorList* validator); + +private: + std::vector<std::unique_ptr<CaseInfo>> m_branchList; + StringResult* m_stringResult; + bool m_stopAtFirst= false; +}; + +#endif // SWITCHCASENODE_H diff --git a/parsingtoolbox.cpp b/parsingtoolbox.cpp index 47405a5..2656fca 100644 --- a/parsingtoolbox.cpp +++ b/parsingtoolbox.cpp @@ -54,6 +54,7 @@ #include "node/splitnode.h" #include "node/startingnode.h" #include "node/stringnode.h" +#include "node/switchcasenode.h" #include "node/uniquenode.h" #include "node/valueslistnode.h" #include "node/variablenode.h" @@ -109,6 +110,7 @@ ParsingToolBox::ParsingToolBox() m_OptionOp.insert(QStringLiteral("g"), Group); m_OptionOp.insert(QStringLiteral("b"), Bind); m_OptionOp.insert(QStringLiteral("o"), Occurences); + m_OptionOp.insert(QStringLiteral("S"), SwitchCaseOption); m_functionMap.insert({QStringLiteral("repeat"), REPEAT}); @@ -901,6 +903,19 @@ bool ParsingToolBox::readAscending(QString& str) } return false; } + +bool ParsingToolBox::readStopAtFirst(QString& str) +{ + if(str.isEmpty()) + return false; + else if(str.at(0) == '^') + { + str= str.remove(0, 1); + return true; + } + return false; +} + Dice::CONDITION_STATE ParsingToolBox::isValidValidator(ExecutionNode* previous, ValidatorList* val) { DiceRollerNode* node= getDiceRollerNode(previous); @@ -1744,6 +1759,15 @@ bool ParsingToolBox::readOption(QString& str, ExecutionNode* previous) //, found= true; } break; + case SwitchCaseOption: + { + auto scNode= new SwitchCaseNode(); + found= readSwitchCaseNode(str, scNode); + previous->setNextNode(scNode); + if(found) + node= scNode; + } + break; case Bind: { BindNode* bindNode= new BindNode(); @@ -1918,6 +1942,27 @@ bool ParsingToolBox::readParameterNode(QString& str, ExecutionNode*& node) return false; } +bool ParsingToolBox::readSwitchCaseNode(QString& str, SwitchCaseNode* node) +{ + bool res= false; + node->setStopAtFirt(ParsingToolBox::readStopAtFirst(str)); + bool hasInstructionBloc= false; + do + { + auto validator= readValidatorList(str); + ExecutionNode* exeNode= nullptr; + hasInstructionBloc= readBlocInstruction(str, exeNode); + if(hasInstructionBloc) + { + node->insertCase(exeNode, validator); + res= true; + } + + } while(hasInstructionBloc); + + return res; +} + bool ParsingToolBox::readBlocInstruction(QString& str, ExecutionNode*& resultnode) { if(str.startsWith('{')) diff --git a/tests/dice/tst_dice.cpp b/tests/dice/tst_dice.cpp index 910fb4d..97feb95 100644 --- a/tests/dice/tst_dice.cpp +++ b/tests/dice/tst_dice.cpp @@ -42,6 +42,7 @@ #include "node/sortresult.h" #include "node/splitnode.h" #include "node/stringnode.h" +#include "node/switchcasenode.h" #include "node/uniquenode.h" #include "operationcondition.h" #include "parsingtoolbox.h" @@ -198,6 +199,9 @@ private slots: void operatoionConditionValidatorTest(); + void switchCaseTest(); + void switchCaseTest_data(); + private: std::unique_ptr<Die> m_die; std::unique_ptr<DiceParser> m_diceParser; @@ -991,7 +995,7 @@ void TestDice::ifTest_data() QTest::addRow("cmd9") << QVector<int>({25, 8, 14}) << onScalar << 1 << "False"; QTest::addRow("cmd10") << QVector<int>({25, 8, 14}) << onScalar << 47 << "True"; - QTest::addRow("cmd11") << QVector<int>({25, 8, 14}) << onEachValue << 47 << "True"; + // QTest::addRow("cmd11") << QVector<int>({25, 8, 14}) << onEachValue << 47 << "True"; } void TestDice::paintTest() {} @@ -1210,6 +1214,86 @@ void TestDice::operatoionConditionValidatorTest() QCOMPARE(value, data); } +void TestDice::switchCaseTest() +{ + using BC= BooleanCondition; + QFETCH(int, value); + QFETCH(QList<BC::LogicOperator>, operatorList); + QFETCH(QList<int>, threshold); + QFETCH(QStringList, values); + QFETCH(QString, expected); + QFETCH(bool, stopatfirt); + + NumberNode* node1= new NumberNode(); + node1->setNumber(value); + + SwitchCaseNode* node2= new SwitchCaseNode(); + node2->setStopAtFirt(stopatfirt); + + int i= 0; + for(const auto& val : values) + { + ValidatorList* validatorList= nullptr; + if(i < threshold.size()) + { + validatorList= makeValidator(QVector<int>{threshold[i]}, operatorList[i]); + } + auto stringNode= new StringNode(); + stringNode->setString(val); + node2->insertCase(stringNode, validatorList); + ++i; + } + + node1->setNextNode(node2); + + node1->run(nullptr); + + auto result= node2->getResult(); + auto stringResult= result->getStringResult(); + + QCOMPARE(stringResult, expected); +} + +void TestDice::switchCaseTest_data() +{ + using BC= BooleanCondition; + QTest::addColumn<int>("value"); + QTest::addColumn<QList<BC::LogicOperator>>("operatorList"); + QTest::addColumn<QList<int>>("threshold"); + QTest::addColumn<QStringList>("values"); + QTest::addColumn<QString>("expected"); + QTest::addColumn<bool>("stopatfirt"); + + QTest::addRow("cmd1") << 75 << QList<BC::LogicOperator>{BC::Equal, BC::Equal} << QList<int>{75, 1} + << QStringList{"a", "b"} << QStringLiteral("a") << false; + + QTest::addRow("cmd2") << 1 << QList<BC::LogicOperator>{BC::Equal, BC::Equal} << QList<int>{75, 1} + << QStringList{"a", "b"} << QStringLiteral("b") << true; + + QTest::addRow("cmd3") << 7 + << QList<BC::LogicOperator>{BC::GreaterThan, BC::GreaterThan, BC::GreaterThan, + BC::GreaterThan, BC::GreaterThan} + << QList<int>{8, 29, 99, 54, 1} << QStringList{"a", "b", "c", "d", "e"} << QStringLiteral("e") + << false; + + QTest::addRow("cmd4") << 75 << QList<BC::LogicOperator>{BC::LesserThan, BC::LesserThan, BC::LesserThan} + << QList<int>{8, 7} << QStringList{"a", "b", "c"} << QStringLiteral("c") << false; + + QTest::addRow("cmd5") << 2 << QList<BC::LogicOperator>{BC::Different, BC::Different} << QList<int>{1, 2} + << QStringList{"a", "b"} << QStringLiteral("a") << false; + + QTest::addRow("cmd6") << 3 + << QList<BC::LogicOperator>{BC::GreaterOrEqual, BC::GreaterOrEqual, BC::GreaterOrEqual, + BC::GreaterOrEqual} + << QList<int>{1, 2, 3, 4} << QStringList{"a", "b", "c", "d"} << QStringLiteral("a,b,c") + << false; + + QTest::addRow("cmd6") << 3 + << QList<BC::LogicOperator>{BC::LesserOrEqual, BC::LesserOrEqual, BC::LesserOrEqual, + BC::LesserOrEqual} + << QList<int>{1, 2, 3, 4} << QStringList{"a", "b", "c", "d"} << QStringLiteral("c") << true; +} + void TestDice::cleanupTestCase() {} QTEST_MAIN(TestDice) |