diff options
Diffstat (limited to 'src/libparser')
104 files changed, 13613 insertions, 0 deletions
diff --git a/src/libparser/CMakeLists.txt b/src/libparser/CMakeLists.txt new file mode 100644 index 0000000..e477696 --- /dev/null +++ b/src/libparser/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.16) + +set(QT_REQUIRED_VERSION "6.2.0") +set(QT_VERSION_MAJOR "6") +find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG REQUIRED COMPONENTS Core Test Gui Svg) +find_package(Qt${QT_VERSION_MAJOR} ${QT_REQUIRED_VERSION} CONFIG COMPONENTS Qml Concurrent) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} result node) + +set(dice_header_path include/diceparser) + +set(dice_public_headers ${dice_header_path}/diceparser_global.h +${dice_header_path}/dicealias.h +${dice_header_path}/diceparser.h +${dice_header_path}/diceparserhelper.h +${dice_header_path}/highlightdice.h +${dice_header_path}/parsingtoolbox.h # should no be public… +) + + +SET( dice_sources + ${CMAKE_CURRENT_SOURCE_DIR}/diceparser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/range.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/booleancondition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validatorlist.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/operationcondition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/die.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/parsingtoolbox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dicealias.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/result/result.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/result/scalarresult.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/result/stringresult.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/result/diceresult.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/countexecutenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/dicerollernode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/executionnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/explodedicenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/helpnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/allsamenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/mergenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/jumpbackwardnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/keepdiceexecnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/listaliasnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/listsetrollnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/numbernode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/parenthesesnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/paintnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/rerolldicenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/scalaroperatornode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/sortresult.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/startingnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/filternode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/stringnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/ifnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/splitnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/groupnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/bind.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/occurencecountnode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/node/uniquenode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/highlightdice.cpp + ${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 + ${CMAKE_CURRENT_SOURCE_DIR}/node/replacevaluenode.cpp +) + +IF(STATIC_BUILD) + add_library(diceparser_static STATIC ${dice_sources} ) + target_include_directories(diceparser_static PRIVATE include) + target_link_libraries(diceparser_static PUBLIC Qt6::Core Qt6::Gui Qt6::Svg) + install(TARGETS diceparser_static + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + + + +add_library(diceparser_shared SHARED ${dice_sources} ${dice_public_headers}) +set_target_properties(diceparser_shared PROPERTIES PUBLIC_HEADER "${public_header_widget}") +target_compile_definitions(diceparser_shared PRIVATE DICEPARSER_LIBRARY) + +target_include_directories(diceparser_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) + +SET_TARGET_PROPERTIES(diceparser_shared PROPERTIES OUTPUT_NAME diceparser CLEAN_DIRECT_OUTPUT 1) +target_link_libraries(diceparser_shared PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Svg) + +set_target_properties(diceparser_shared PROPERTIES VERSION ${PROJECT_VERSION}) +set_target_properties(diceparser_shared PROPERTIES SOVERSION 1) + + +include(GNUInstallDirs) + + +configure_file(diceparser.pc.in diceparser.pc @ONLY) + +install(TARGETS diceparser_shared + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/diceparser.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + +if(Qt${QT_VERSION_MAJOR}Qml_FOUND) + set(dice_header_qobject include/diceparser_qobject/diceparser_qobject_global.h include/diceparser_qobject/qmltypesregister.h include/diceparser_qobject/diceroller.h) + set(dice_source_qobject qmltypesregister.cpp diceroller.cpp) + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTOUIC ON) + set(CMAKE_AUTORCC ON) + set(CMAKE_INCLUDE_CURRENT_DIR ON) + add_library(diceparser_qobject SHARED ${dice_header_qobject} ${dice_source_qobject}) + set_target_properties(diceparser_qobject PROPERTIES PUBLIC_HEADER "${dice_header_qobject}") + target_compile_definitions(diceparser_qobject PRIVATE DICEPARSER_QOBJECT_LIBRARY) + target_link_libraries(diceparser_qobject PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Qml Qt${QT_VERSION_MAJOR}::Concurrent PRIVATE diceparser_shared) + message("Messages diceparser ${CMAKE_CURRENT_SOURCE_DIR}/include") + + target_include_directories(diceparser_qobject PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) + + + install(TARGETS diceparser_qobject + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif() + + + diff --git a/src/libparser/booleancondition.cpp b/src/libparser/booleancondition.cpp new file mode 100644 index 0000000..91be35c --- /dev/null +++ b/src/libparser/booleancondition.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "booleancondition.h" + +Dice::CONDITION_STATE testEqual(bool insideRange, const std::pair<qint64, qint64>& range) +{ + if(!insideRange) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(insideRange && (range.first == range.second)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testGreatherThan(qint64 value, const std::pair<qint64, qint64>& range) +{ + if(value >= std::max(range.first, range.second)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(value < std::min(range.first, range.second)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testLesserThan(qint64 value, const std::pair<qint64, qint64>& range) +{ + if(value <= std::min(range.first, range.second)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(value > std::max(range.first, range.second)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testGreaterOrEqual(qint64 value, const std::pair<qint64, qint64>& range) +{ + if(value > std::max(range.first, range.second)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(value <= std::min(range.first, range.second)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testLesserOrEqual(qint64 value, const std::pair<qint64, qint64>& range) +{ + if(value < std::min(range.first, range.second)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(value >= std::max(range.first, range.second)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testDifferent(bool inside, const std::pair<qint64, qint64>& range) +{ + if(inside && (range.first == range.second)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(!inside) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +BooleanCondition::BooleanCondition() : m_operator(Dice::CompareOperator::Equal) {} + +BooleanCondition::~BooleanCondition() +{ + if(m_value != nullptr) + { + delete m_value; + m_value= nullptr; + } +} +qint64 BooleanCondition::hasValid(Die* b, bool recursive, bool unhighlight) const +{ + QList<qint64> listValues; + if(m_conditionType == Dice::OnEachValue) + { + listValues.append(b->getValue()); + } + else if(recursive) + { + listValues= b->getListValue(); + } + else + { + listValues.append(b->getLastRolledValue()); + } + + qint64 sum= 0; + auto valueScalar= valueToScalar(); + for(qint64& value : listValues) + { + switch(m_operator) + { + case Dice::CompareOperator::Equal: + sum+= (value == valueScalar) ? 1 : 0; + break; + case Dice::CompareOperator::GreaterThan: + sum+= (value > valueScalar) ? 1 : 0; + break; + case Dice::CompareOperator::LesserThan: + sum+= (value < valueScalar) ? 1 : 0; + break; + case Dice::CompareOperator::GreaterOrEqual: + sum+= (value >= valueScalar) ? 1 : 0; + break; + case Dice::CompareOperator::LesserOrEqual: + sum+= (value <= valueScalar) ? 1 : 0; + break; + case Dice::CompareOperator::Different: + sum+= (value != valueScalar) ? 1 : 0; + break; + } + } + if((unhighlight) && (sum == 0)) + { + b->setHighlighted(false); + } + else + { + b->setHighlighted(true); + } + + return sum; +} + +void BooleanCondition::setOperator(Dice::CompareOperator m) +{ + m_operator= m; +} + +void BooleanCondition::setValueNode(ExecutionNode* v) +{ + m_value= v; +} +QString BooleanCondition::toString() +{ + QString str(""); + switch(m_operator) + { + case Dice::CompareOperator::Equal: + str.append(QStringLiteral("=")); + break; + case Dice::CompareOperator::GreaterThan: + str.append(QStringLiteral(">")); + break; + case Dice::CompareOperator::LesserThan: + str.append(QStringLiteral("<")); + break; + case Dice::CompareOperator::GreaterOrEqual: + str.append(QStringLiteral(">=")); + break; + case Dice::CompareOperator::LesserOrEqual: + str.append(QStringLiteral("<=")); + break; + case Dice::CompareOperator::Different: + str.append(QStringLiteral("!=")); + break; + } + return QStringLiteral("[%1%2]").arg(str).arg(valueToScalar()); +} + +Dice::CONDITION_STATE BooleanCondition::isValidRangeSize(const std::pair<qint64, qint64>& range) const +{ + Dice::CONDITION_STATE state; + auto valueScalar= valueToScalar(); + qint64 boundValue= qBound(range.first, valueScalar, range.second); + bool isInsideRange= (boundValue == valueScalar); + switch(m_operator) + { + case Dice::CompareOperator::Equal: + state= testEqual(isInsideRange, range); //(isInsideRange && (range.first != range.second)) ? ; + break; + case Dice::CompareOperator::GreaterThan: + state= testGreatherThan(valueScalar, range); + break; + case Dice::CompareOperator::LesserThan: + state= testLesserThan(valueScalar, range); + break; + case Dice::CompareOperator::GreaterOrEqual: + state= testGreaterOrEqual(valueScalar, range); + break; + case Dice::CompareOperator::LesserOrEqual: + state= testLesserOrEqual(valueScalar, range); + break; + case Dice::CompareOperator::Different: + state= testDifferent(isInsideRange, range); + break; + } + return state; +} +Validator* BooleanCondition::getCopy() const +{ + BooleanCondition* val= new BooleanCondition(); + val->setOperator(m_operator); + val->setValueNode(m_value->getCopy()); + return val; +} +qint64 BooleanCondition::valueToScalar() const +{ + if(m_value == nullptr) + return 0; + + m_value->run(nullptr); + auto result= m_value->getResult(); + if(result) + return result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + else + return 0; +} diff --git a/src/libparser/booleancondition.h b/src/libparser/booleancondition.h new file mode 100644 index 0000000..d49c5fe --- /dev/null +++ b/src/libparser/booleancondition.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 BOOLEANCONDITION_H +#define BOOLEANCONDITION_H + +#include "diceparser/diceparserhelper.h" +#include "node/executionnode.h" +#include "validator.h" +#include <Qt> +/** + * @brief The BooleanCondition class is a Validator class checking validity from logic expression. + * It manages many operators (see : @ref LogicOperator). + */ +class BooleanCondition : public Validator +{ +public: + BooleanCondition(); + virtual ~BooleanCondition() override; + + virtual qint64 hasValid(Die* b, bool recursive, bool unhighlight= false) const override; + + void setOperator(Dice::CompareOperator m); + void setValueNode(ExecutionNode*); + QString toString() override; + + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const override; + /** + * @brief getCopy + * @return + */ + virtual Validator* getCopy() const override; + +private: + qint64 valueToScalar() const; + +private: + Dice::CompareOperator m_operator; + ExecutionNode* m_value= nullptr; +}; + +// Q_DECLARE_METATYPE(BooleanCondition::LogicOperator) +#endif // BOOLEANCONDITION_H diff --git a/src/libparser/compositevalidator.cpp b/src/libparser/compositevalidator.cpp new file mode 100644 index 0000000..b0f127d --- /dev/null +++ b/src/libparser/compositevalidator.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "compositevalidator.h" + +#include <map> + +CompositeValidator::CompositeValidator() {} + +CompositeValidator::~CompositeValidator() +{ + qDeleteAll(m_validatorList); +} +qint64 CompositeValidator::hasValid(Die* b, bool recursive, bool unhighlight) const +{ + int i= 0; + qint64 sum= 0; + bool highLight= false; + for(auto& validator : m_validatorList) + { + qint64 val= validator->hasValid(b, recursive, unhighlight); + if(i == 0) + { + sum= val; + if(b->isHighlighted()) + { + highLight= b->isHighlighted(); + } + } + else + { + switch(m_operators.at(i - 1)) + { + case OR: + sum|= val; + + if(highLight) + { + b->setHighlighted(highLight); + } + break; + case EXCLUSIVE_OR: + sum^= val; /// @todo may required to be done by hand + break; + case AND: + sum&= val; + break; + default: + break; + } + } + ++i; + } + + return sum; +} + +QString CompositeValidator::toString() +{ + // m_validatorList + QStringList validatorsTextList; + std::transform(m_validatorList.begin(), m_validatorList.end(), std::back_inserter(validatorsTextList), + [](Validator* validator) { return validator->toString(); }); + QStringList operatorTextList; + std::transform( + m_operators.begin(), m_operators.end(), std::back_inserter(operatorTextList), [](LogicOperation validator) { + static std::map<LogicOperation, QString> map({{OR, "|"}, {EXCLUSIVE_OR, "^"}, {AND, "&"}, {NONE, ""}}); + return map[validator]; + }); + + if(validatorsTextList.size() - 1 != operatorTextList.size()) + return QString("Error - operator and validator count don't fit"); + + QStringList result; + int i= 0; + for(auto text : validatorsTextList) + { + result << text << operatorTextList[i]; + + ++i; + } + return result.join(" "); +} + +Dice::CONDITION_STATE testAND(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == Dice::CONDITION_STATE::UNREACHABLE || current == Dice::CONDITION_STATE::UNREACHABLE) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(before == Dice::CONDITION_STATE::ALWAYSTRUE && current == Dice::CONDITION_STATE::ALWAYSTRUE) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testOR(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == Dice::CONDITION_STATE::UNREACHABLE && current == Dice::CONDITION_STATE::UNREACHABLE) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(before == Dice::CONDITION_STATE::ALWAYSTRUE || current == Dice::CONDITION_STATE::ALWAYSTRUE) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testXOR(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == current + && (before == Dice::CONDITION_STATE::UNREACHABLE || before == Dice::CONDITION_STATE::ALWAYSTRUE)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if((before != current) + && (before == Dice::CONDITION_STATE::ALWAYSTRUE || before == Dice::CONDITION_STATE::UNREACHABLE) + && (before != Dice::CONDITION_STATE::REACHABLE || current != Dice::CONDITION_STATE::REACHABLE)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE CompositeValidator::isValidRangeSize(const std::pair<qint64, qint64>& range) const +{ + std::vector<Dice::CONDITION_STATE> vec; + std::transform( + m_validatorList.begin(), m_validatorList.end(), std::back_inserter(vec), + [range](Validator* validator) -> Dice::CONDITION_STATE { return validator->isValidRangeSize(range); }); + + auto itError= std::find(vec.begin(), vec.end(), Dice::CONDITION_STATE::ERROR_STATE); + + if((static_cast<int>(vec.size()) != m_operators.size() + 1) || (itError != vec.end())) + { + return Dice::CONDITION_STATE::ERROR_STATE; + } + + std::size_t i= 0; + Dice::CONDITION_STATE val= Dice::CONDITION_STATE::ERROR_STATE; + for(const auto& op : m_operators) + { + auto currentState= vec[i + 1]; + if(i == 0) + { + val= vec[i]; + } + switch(op) + { + case OR: + val= testAND(val, currentState); + break; + case EXCLUSIVE_OR: + val= testOR(val, currentState); + break; + case AND: + val= testXOR(val, currentState); + break; + case NONE: + val= Dice::CONDITION_STATE::ERROR_STATE; + break; + } + + ++i; + } + return val; +} + +void CompositeValidator::setOperationList(const QVector<LogicOperation>& m) +{ + m_operators= m; +} + +void CompositeValidator::setValidatorList(const QList<Validator*>& valids) +{ + qDeleteAll(m_validatorList); + m_validatorList= valids; +} +Validator* CompositeValidator::getCopy() const +{ + CompositeValidator* val= new CompositeValidator(); + val->setOperationList(m_operators); + val->setValidatorList(m_validatorList); + return val; +} diff --git a/src/libparser/compositevalidator.h b/src/libparser/compositevalidator.h new file mode 100644 index 0000000..c2c066a --- /dev/null +++ b/src/libparser/compositevalidator.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 COMPOSITEVALIDATOR_H +#define COMPOSITEVALIDATOR_H + +#include <QList> +#include <QString> +#include <QVector> +#include <Qt> + +#include "validator.h" +/** + * @brief The BooleanCondition class is a Validator class checking validity from logic expression. + * It manages many operators (see : @ref LogicOperator). + */ +class CompositeValidator : public Validator +{ +public: + enum LogicOperation + { + OR, + EXCLUSIVE_OR, + AND, + NONE + }; + CompositeValidator(); + virtual ~CompositeValidator() override; + + virtual qint64 hasValid(Die* b, bool recursive, bool unhighlight= false) const override; + + void setOperationList(const QVector<LogicOperation>& m); + void setValidatorList(const QList<Validator*>& valids); + + QString toString() override; + + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const override; + + virtual Validator* getCopy() const override; + +private: + QVector<LogicOperation> m_operators; + QList<Validator*> m_validatorList; +}; + +#endif // BOOLEANCONDITION_H diff --git a/src/libparser/dicealias.cpp b/src/libparser/dicealias.cpp new file mode 100644 index 0000000..a5c079d --- /dev/null +++ b/src/libparser/dicealias.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 <QRegularExpression> +#include <diceparser/dicealias.h> + +#include <QDebug> + +QString makeReplament(const QString& pattern, const QString& command, QString cmd) +{ + auto hasPattern= cmd.contains(pattern); + if(hasPattern) + { + auto idxPattern= cmd.indexOf(pattern); + std::vector<std::pair<int, int>> quotes; + + int pos= 0; + bool open= true; + while(pos != -1 && pos < cmd.size()) + { + auto oldPos= pos; + pos= cmd.indexOf("\"", pos); + if(open && pos != -1) + open= false; + else if(pos != -1) + { + quotes.push_back({oldPos, pos}); + } + + if(pos != -1) + pos+= 1; + } + auto hasQuote= false; + for(auto range : quotes) + { + if(idxPattern < range.second && idxPattern >= range.first) + hasQuote= true; + } + + auto hasVariable= cmd.contains("${"); + auto commentPos= cmd.lastIndexOf("#"); + + if(!hasQuote && !hasVariable) + { + cmd.replace(pattern, command); + } + else + { + std::vector<int> patternPosList; + std::vector<std::pair<int, int>> variablePos; + + int pos= 0; + QRegularExpressionMatch match; + while(pos != -1) + { + auto start= cmd.indexOf(QRegularExpression("\\${\\N+}"), pos, &match); + if(start >= 0) + { + auto end= start + match.captured().length(); + variablePos.push_back(std::make_pair(start, end)); + pos= end + 1; + } + else + { + pos= start; + } + } + + pos= 0; + while(pos != -1) + { + auto start= cmd.indexOf("\"", pos); + if(start >= 0) + { + auto end= cmd.indexOf("\"", start + 1); + variablePos.push_back(std::make_pair(start, end)); + pos= end + 1; + } + else + { + pos= start; + } + } + pos= 0; + while((pos= cmd.indexOf(pattern, pos)) && pos != -1) + { + bool isInsidePair= false; + for(auto pair : variablePos) + { + if(!isInsidePair) + isInsidePair= (pos > pair.first && pos < pair.second); + + if(commentPos >= 0 && pos > commentPos) + isInsidePair= true; + } + if(!isInsidePair) + patternPosList.push_back(pos); + + pos+= 1; + } + + // TODO to be replace by C++14 when it is ready + for(auto i= patternPosList.rbegin(); i != patternPosList.rend(); ++i) + { + cmd.replace(*i, 1, command); + } + } + } + return cmd; +} + +DiceAlias::DiceAlias(QString pattern, QString command, QString comment, bool isReplace, bool isEnable) + : m_pattern(pattern) + , m_command(command) + , m_comment(comment) + , m_type(isReplace ? REPLACE : REGEXP) + , m_isEnable(isEnable) +{ +} + +DiceAlias::~DiceAlias() +{ + // qDebug() << "destructeur of alias!" << this; +} + +DiceAlias::DiceAlias(const DiceAlias& alias) +{ + m_command= alias.command(); + m_comment= alias.comment(); + m_pattern= alias.pattern(); + m_isEnable= alias.isEnable(); + m_type= alias.isReplace() ? REPLACE : REGEXP; +} + +bool DiceAlias::resolved(QString& str) +{ + if(!m_isEnable) + return false; + + if((m_type == REPLACE) && (str.contains(m_pattern))) + { + str= makeReplament(m_pattern, m_command, str); + return true; + } + else if(m_type == REGEXP) + { + QRegularExpression exp(m_pattern); + str.replace(exp, m_command); + return true; + } + return false; +} + +void DiceAlias::setCommand(QString command) +{ + m_command= command; +} + +void DiceAlias::setPattern(const QString& pattern) +{ + m_pattern= pattern; +} + +void DiceAlias::setType(RESOLUTION_TYPE type) +{ + m_type= type; +} +QString DiceAlias::command() const +{ + return m_command; +} + +QString DiceAlias::pattern() const +{ + return m_pattern; +} + +bool DiceAlias::isReplace() const +{ + return (m_type == REPLACE) ? true : false; +} + +void DiceAlias::setReplace(bool b) +{ + if(b) + { + m_type= REPLACE; + } + else + { + m_type= REGEXP; + } +} + +bool DiceAlias::isEnable() const +{ + return m_isEnable; +} + +void DiceAlias::setEnable(bool b) +{ + m_isEnable= b; +} + +QString DiceAlias::comment() const +{ + return m_comment; +} + +void DiceAlias::setComment(const QString& comment) +{ + m_comment= comment; +} diff --git a/src/libparser/diceparser.cpp b/src/libparser/diceparser.cpp new file mode 100644 index 0000000..af86acc --- /dev/null +++ b/src/libparser/diceparser.cpp @@ -0,0 +1,271 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 <diceparser/diceparser.h> + +#include <QDebug> +#include <QFile> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QObject> +#include <QStringList> +#include <functional> +#include <numeric> + +#include "booleancondition.h" +#include "range.h" +#include "result/stringresult.h" +#include "validator.h" +#include <diceparser/dicealias.h> +#include <diceparser/parsingtoolbox.h> + +#define DEFAULT_FACES_NUMBER 10 + +DiceParser::DiceParser() : m_parsingToolbox(new ParsingToolBox()) {} +DiceParser::~DiceParser() {} + +const QList<DiceAlias*>& DiceParser::constAliases() const +{ + return m_parsingToolbox->getAliases(); +} + +QList<DiceAlias*>* DiceParser::aliases() const +{ + return m_parsingToolbox->aliases(); +} + +void DiceParser::cleanAliases() +{ + m_parsingToolbox->cleanUpAliases(); +} +void DiceParser::insertAlias(DiceAlias* dice, int i) +{ + m_parsingToolbox->insertAlias(dice, i); +} + +bool DiceParser::parseLine(QString str, bool allowAlias) +{ + if(allowAlias) + { + str= m_parsingToolbox->convertAlias(str); + } + m_parsingToolbox->clearUp(); + m_command= str; + auto instructions= m_parsingToolbox->readInstructionList(str, true); + m_command.remove(m_parsingToolbox->getComment()); + bool value= !instructions.empty(); + if(!value) + { + m_parsingToolbox->addError(Dice::ERROR_CODE::NOTHING_UNDERSTOOD, + QObject::tr("Nothing was understood. To roll dice: !1d6 - full documation: " + "<a " + "href=\"https://github.com/Rolisteam/DiceParser/blob/master/" + "HelpMe.md\">https://github.com/" + "Rolisteam/DiceParser/blob/master/HelpMe.md</a>")); + } + else if(value && !str.isEmpty()) + { + auto i= m_command.size() - str.size(); + m_parsingToolbox->addWarning( + Dice::ERROR_CODE::UNEXPECTED_CHARACTER, + QObject::tr("Unexpected character at %1 - end of command was ignored \"%2\"").arg(i).arg(str)); + } + + if(m_parsingToolbox->hasError()) + value= false; + + return value; +} + +QString DiceParser::convertAlias(const QString& cmd) const +{ + return m_parsingToolbox->convertAlias(cmd); +} + +void DiceParser::start() +{ + for(auto start : m_parsingToolbox->getStartNodes()) + { + start->run(); + } +} + +QString DiceParser::diceCommand() const +{ + return m_command; +} + +bool DiceParser::hasIntegerResultNotInFirst() const +{ + return m_parsingToolbox->hasIntegerResultNotInFirst(); +} + +bool DiceParser::hasDiceResult() const +{ + return m_parsingToolbox->hasDiceResult(); +} +bool DiceParser::hasStringResult() const +{ + return m_parsingToolbox->hasStringResult(); +} + +int DiceParser::startNodeCount() const +{ + return static_cast<int>(m_parsingToolbox->getStartNodes().size()); +} + +QList<qreal> DiceParser::scalarResultsFromEachInstruction() const +{ + return m_parsingToolbox->scalarResultsFromEachInstruction(); +} + +QStringList DiceParser::stringResultFromEachInstruction(bool& hasAlias) const +{ + return m_parsingToolbox->allFirstResultAsString(hasAlias); +} + +void DiceParser::diceResultFromEachInstruction(QList<ExportedDiceResult>& resultList) const +{ + resultList= m_parsingToolbox->diceResultFromEachInstruction(); +} + +QString DiceParser::comment() const +{ + return m_parsingToolbox->getComment(); +} + +void DiceParser::setComment(const QString& comment) +{ + m_parsingToolbox->setComment(comment); +} + +QMap<Dice::ERROR_CODE, QString> DiceParser::errorMap() const +{ + QMap<Dice::ERROR_CODE, QString> map; + + for(auto start : m_parsingToolbox->getStartNodes()) + { + auto mapTmp= start->getExecutionErrorMap(); + auto keys= mapTmp.keys(); + for(auto& key : keys) + { + map.insert(key, mapTmp[key]); + } + } + return map; +} +QString DiceParser::humanReadableError() const +{ + auto parsingError= m_parsingToolbox->getErrorList(); + QString str; + std::for_each(parsingError.begin(), parsingError.end(), + [&str](const QString& text) + { + str.append(text); + str.append(QStringLiteral("\n")); + }); + + /// list + auto errMap= errorMap(); + std::for_each(errMap.begin(), errMap.end(), + [&str](const QString& text) + { + str.append(text); + str.append(QStringLiteral("\n")); + }); + return str; +} + +QString DiceParser::humanReadableWarning() const +{ + auto warningMap= m_parsingToolbox->getWarningList(); + QMapIterator<Dice::ERROR_CODE, QString> i(warningMap); + QString str(""); + while(i.hasNext()) + { + i.next(); + str.append(i.value()); + str.append(QStringLiteral("\n")); + } + return str; +} + +QString DiceParser::finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize) const +{ + return m_parsingToolbox->finalStringResult(colorize); +} + +QString DiceParser::resultAsJSon(std::function<QString(const QString&, const QString&, bool)> colorize, + bool removeUnhighligthed) const +{ + QJsonObject obj; + QJsonArray instructions; + for(auto start : m_parsingToolbox->getStartNodes()) + { + QJsonObject inst; + + m_parsingToolbox->addResultInJson(inst, Dice::RESULT_TYPE::SCALAR, "scalar", start, true); + m_parsingToolbox->addResultInJson(inst, Dice::RESULT_TYPE::STRING, "string", start, false); + m_parsingToolbox->addDiceResultInJson(inst, start, colorize); + + instructions.append(inst); + } + obj["instructions"]= instructions; + obj["comment"]= m_parsingToolbox->getComment(); + obj["error"]= humanReadableError(); + obj["scalar"]= m_parsingToolbox->finalScalarResult().first; + obj["string"]= m_parsingToolbox->finalStringResult(colorize, removeUnhighligthed); + obj["warning"]= humanReadableWarning(); + obj["command"]= m_command; + + QJsonDocument doc; + doc.setObject(obj); + return doc.toJson(); +} + +void DiceParser::writeDownDotTree(QString filepath) +{ + if(m_parsingToolbox->getStartNodes().empty()) + return; + + QString str(QStringLiteral("digraph ExecutionTree {\n")); + for(auto start : m_parsingToolbox->getStartNodes()) + { + start->generateDotTree(str); + } + str.append(QStringLiteral("}\n")); + + QFile file(filepath); + if(file.open(QIODevice::WriteOnly)) + { + QTextStream in(&file); + in << str; + } +} +void DiceParser::setPathToHelp(QString l) +{ + m_parsingToolbox->setHelpPath(l); +} +void DiceParser::setVariableDictionary(const QHash<QString, QString>& variables) +{ + ParsingToolBox::setVariableHash(variables); +} diff --git a/src/libparser/diceparser.pc.in b/src/libparser/diceparser.pc.in new file mode 100644 index 0000000..61b5aea --- /dev/null +++ b/src/libparser/diceparser.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ + +Requires: +Libs: -L${libdir} -ldiceparser +Cflags: -I${includedir} diff --git a/src/libparser/diceroller.cpp b/src/libparser/diceroller.cpp new file mode 100644 index 0000000..e1c8ded --- /dev/null +++ b/src/libparser/diceroller.cpp @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (C) 2017 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * Rolisteam 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 <diceparser_qobject/diceroller.h> + +#include <QJsonObject> +#include <QtConcurrent> + +DiceRoller::DiceRoller(QObject* parent) : QObject(parent) {} + +QString DiceRoller::diceList() const +{ + return m_diceList; +} + +QString DiceRoller::resultStr() const +{ + return m_resultStr; +} + +QString DiceRoller::command() const +{ + return m_command; +} + +qreal DiceRoller::result() const +{ + return m_result; +} + +void DiceRoller::setCommand(const QString& cmd) +{ + if(m_command != cmd) + { + m_command= cmd; + emit commandChanged(); + } +} + +void DiceRoller::readErrorAndWarning() +{ + setError( + tr("Error:\n%1\nWarnings:\n%2").arg(m_diceparser.humanReadableError(), m_diceparser.humanReadableWarning())); +} + +void DiceRoller::start() +{ + auto future= QtConcurrent::run( + [this]() + { + if(m_diceparser.parseLine(m_command)) + { + m_diceparser.start(); + readErrorAndWarning(); + auto jsonstr + = m_diceparser.resultAsJSon([](const QString& value, const QString&, bool) { return value; }); + QJsonDocument doc= QJsonDocument::fromJson(jsonstr.toLocal8Bit()); + auto json= doc.object(); + m_result= json["scalar"].toString().toDouble(); + emit resultChanged(); + } + }); +} + +QString DiceRoller::error() const +{ + return m_error; +} + +QList<DiceAlias*>* DiceRoller::aliases() const +{ + return m_diceparser.aliases(); +} + +DiceParser* DiceRoller::parser() +{ + return &m_diceparser; +} + +void DiceRoller::setError(const QString& error) +{ + if(m_error == error) + return; + + m_error= error; + emit errorOccurs(); +} + +/*QString DiceRoller::diceToText(QList<ExportedDiceResult>& diceList) +{ + QStringList global; + for(auto& dice : diceList) + { + QStringList resultGlobal; + auto const& keys= dice.keys(); + for(auto& face : keys) + { + QStringList result; + auto list= dice.value(face); + for(auto diceResult : list) + { + for(const HighLightDice& tmp : diceResult) + { + QStringList diceListStr; + QStringList diceListChildren; + int i= 0; + for(qint64& dievalue : tmp.result()) + { + QString prefix("%1"); + if(i == 0) + { + diceListStr << prefix.arg(QString::number(dievalue)); + } + else + { + diceListChildren << prefix.arg(QString::number(dievalue)); + } + ++i; + } + if(!diceListChildren.isEmpty()) + { + diceListStr << QString("[%1]").arg(diceListChildren.join(' ')); + } + result << diceListStr.join(' '); + } + +if(keys.size() > 1) +{ + resultGlobal << QString(" d%2:(%1)").arg(result.join(',')).arg(face); +} +else +{ + resultGlobal << result; +} +} +} +global << resultGlobal.join(' '); +} +return global.join(" ; "); +}*/ + +/*if(m_diceparser.parseLine(m_command)) + { + m_diceparser.start(); + if(m_diceparser.errorMap().isEmpty()) + { + bool homogeneous; + QList<ExportedDiceResult> list; + m_diceparser.lastDiceResult(list, homogeneous); + QString diceText= diceToText(list); + QString scalarText; + QString str; + +qreal result= 0; +QString resultStr; +if(m_diceparser.hasIntegerResultNotInFirst()) +{ + auto values= m_diceparser.lastIntegerResults(); + QStringList strLst; + for(auto& val : values) + { + result+= val; + strLst << QString::number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); +} +else if(!list.isEmpty()) +{ + auto values= m_diceparser.getSumOfDiceResult(); + QStringList strLst; + for(auto val : values) + { + result+= val; + strLst << QString::number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); +} + +if(m_diceparser.hasStringResult()) +{ + bool ok; + QStringList allStringlist= m_diceparser.getAllStringResult(ok); + QString stringResult= allStringlist.join(" ; "); + stringResult.replace("%1", scalarText); + stringResult.replace("%2", diceText.trimmed()); + str= stringResult; +} +else +{ + resultStr= scalarText; +} +if(!m_diceparser.getComment().isEmpty()) +{ + resultStr+= m_diceparser.getComment() + "\n"; +} +resultStr+= str + "\n"; +m_resultStr= resultStr; +m_result= result; +m_diceList= diceText.trimmed(); +emit resultStrChanged(); +emit resultChanged(); +emit diceListChanged(); +} +} + +if(!m_diceparser.getErrorMap().isEmpty()) +{ + auto errors= m_diceparser.getErrorMap(); + setError(errors.first()); +} +*/ diff --git a/src/libparser/die.cpp b/src/libparser/die.cpp new file mode 100644 index 0000000..4214b97 --- /dev/null +++ b/src/libparser/die.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "die.h" + +#include <QDateTime> +#include <QDebug> +#include <QUuid> +#include <algorithm> +#include <array> +#include <chrono> + +void Die::buildSeed() +{ + static bool init= false; + if(init) + return; + std::array<int, std::mt19937::state_size> seed_data; + std::random_device r; + std::generate_n(seed_data.data(), seed_data.size(), std::ref(r)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + s_rng= std::mt19937(seq); + init= true; +} + +std::mt19937 Die::s_rng; + +Die::Die() + : m_uuid(QUuid::createUuid().toString(QUuid::WithoutBraces)) + , m_hasValue(false) + , m_displayStatus(false) + , m_highlighted(true) + , m_base(1) + , m_color("") + , m_op(Dice::ArithmeticOperator::PLUS) //,m_mt(m_randomDevice) +{ + buildSeed(); +} + +Die::Die(const Die& die) +{ + m_value= die.m_value; + m_rollResult= die.m_rollResult; + m_selected= die.m_selected; + m_hasValue= die.m_hasValue; + m_uuid= die.m_uuid; + m_displayStatus= die.m_displayStatus; + m_maxValue= die.m_maxValue; + m_highlighted= die.m_highlighted; + m_base= die.m_base; + m_color= die.getColor(); + m_op= die.getOp(); + // auto seed= std::chrono::high_resolution_clock::now().time_since_epoch().count(); + // m_rng= std::mt19937(quintptr(this) + static_cast<unsigned long long>(seed)); +} + +void Die::setValue(qint64 r) +{ + m_value= r; + m_hasValue= true; +} + +void Die::insertRollValue(qint64 r) +{ + m_rollResult.append(r); +} + +void Die::setSelected(bool b) +{ + m_selected= b; +} + +bool Die::isSelected() const +{ + return m_selected; +} +qint64 Die::getValue() const +{ + if(m_hasValue) + { + return m_value; + } + else + { + qint64 value= 0; + int i= 0; + for(qint64 tmp : m_rollResult) + { + if(i > 0) + { + switch(m_op) + { + case Dice::ArithmeticOperator::PLUS: + value+= tmp; + break; + case Dice::ArithmeticOperator::MULTIPLICATION: + value*= tmp; + break; + case Dice::ArithmeticOperator::MINUS: + value-= tmp; + break; + case Dice::ArithmeticOperator::INTEGER_DIVIDE: + case Dice::ArithmeticOperator::DIVIDE: + if(tmp != 0) + { + value/= tmp; + } + else + { + // error(); + } + break; + case Dice::ArithmeticOperator::POW: + value= static_cast<qint64>(std::pow(value, tmp)); + break; + } + } + else + { + value= tmp; + } + ++i; + } + return value; + } +} +QList<qint64> Die::getListValue() const +{ + return m_rollResult; +} +bool Die::hasChildrenValue() +{ + return m_rollResult.size() > 1 ? true : false; +} +void Die::replaceLastValue(qint64 value) +{ + m_rollResult.removeLast(); + insertRollValue(value); +} + +void Die::roll(bool adding) +{ + if(m_maxValue != 0) + { + // quint64 value=(qrand()%m_faces)+m_base; + std::uniform_int_distribution<qint64> dist(m_base, m_maxValue); + qint64 value= dist(s_rng); + if((adding) || (m_rollResult.isEmpty())) + { + insertRollValue(value); + } + else + { + replaceLastValue(value); + } + } +} + +quint64 Die::getFaces() const +{ + return std::abs(m_maxValue - m_base) + 1; +} +qint64 Die::getLastRolledValue() +{ + if(!m_rollResult.isEmpty()) + { + return m_rollResult.last(); + } + else + return 0; +} +bool Die::hasBeenDisplayed() const +{ + return m_displayStatus; +} +void Die::displayed() +{ + setDisplayed(true); +} +void Die::setDisplayed(bool b) +{ + m_displayStatus= b; +} +void Die::setHighlighted(bool a) +{ + m_highlighted= a; +} + +bool Die::isHighlighted() const +{ + return m_highlighted; +} +void Die::setBase(qint64 base) +{ + m_base= base; +} +qint64 Die::getBase() +{ + return m_base; +} +QString Die::getColor() const +{ + return m_color; +} + +void Die::setColor(const QString& color) +{ + m_color= color; +} + +qint64 Die::getMaxValue() const +{ + return m_maxValue; +} + +void Die::setMaxValue(const qint64& maxValue) +{ + m_maxValue= maxValue; +} + +Dice::ArithmeticOperator Die::getOp() const +{ + return m_op; +} + +void Die::setOp(const Dice::ArithmeticOperator& op) +{ + m_op= op; +} +QString Die::getUuid() const +{ + return m_uuid; +} + +void Die::setUuid(const QString& uuid) +{ + m_uuid= uuid; +} diff --git a/src/libparser/die.h b/src/libparser/die.h new file mode 100644 index 0000000..2da2006 --- /dev/null +++ b/src/libparser/die.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 DIE_H +#define DIE_H + +#include <QList> +#include <QString> +#include <random> + +#include "diceparser/diceparserhelper.h" +/** + * @brief The Die class implements all methods required from a die. You must set the Faces first, then you can roll it + * and roll it again, to add or replace the previous result. + */ +class Die +{ +public: + /** + * @brief Die + */ + Die(); + /** + * @brief Die + */ + Die(const Die&); + /** + * @brief setValue + * @param r + */ + void setValue(qint64 r); + /** + * @brief insertRollValue + * @param r + */ + void insertRollValue(qint64 r); + /** + * @brief setSelected + * @param b + */ + void setSelected(bool b); + /** + * @brief isSelected + * @return + */ + bool isSelected() const; + /** + * @brief getValue + * @return + */ + qint64 getValue() const; + /** + * @brief getListValue + * @return + */ + QList<qint64> getListValue() const; + /** + * @brief hasChildrenValue + * @return + */ + bool hasChildrenValue(); + + /** + * @brief roll + * @param adding + */ + void roll(bool adding= false); + /** + * @brief replaceLastValue + * @param value + */ + void replaceLastValue(qint64 value); + + /** + * @brief getLastRolledValue + * @return + */ + qint64 getLastRolledValue(); + /** + * @brief getFaces + * @return + */ + quint64 getFaces() const; + /** + * @brief hasBeenDisplayed + * @return + */ + bool hasBeenDisplayed() const; + /** + * @brief displayed + */ + void displayed(); + /** + * @brief setHighlighted + */ + void setHighlighted(bool); + /** + * @brief isHighlighted + * @return + */ + bool isHighlighted() const; + + /** + * @brief setBase + */ + void setBase(qint64); + qint64 getBase(); + + QString getColor() const; + void setColor(const QString& color); + + qint64 getMaxValue() const; + void setMaxValue(const qint64& maxValue); + + Dice::ArithmeticOperator getOp() const; + void setOp(const Dice::ArithmeticOperator& op); + void setDisplayed(bool b); + + QString getUuid() const; + void setUuid(const QString& uuid); + + static void buildSeed(); + +private: + QString m_uuid; + qint64 m_value{0}; + QList<qint64> m_rollResult; + bool m_selected{false}; + bool m_hasValue{false}; + bool m_displayStatus{false}; + bool m_highlighted{true}; + qint64 m_maxValue{0}; + qint64 m_base{0}; + qint64 m_occurence{1}; + QString m_color; + + Dice::ArithmeticOperator m_op; + + static std::mt19937 s_rng; +}; + +#endif // DIE_H diff --git a/src/libparser/highlightdice.cpp b/src/libparser/highlightdice.cpp new file mode 100644 index 0000000..e92451f --- /dev/null +++ b/src/libparser/highlightdice.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2016 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 <diceparser/highlightdice.h> + +HighLightDice::HighLightDice(QList<qint64> result, bool isHighlighted, QString color, bool displayed, quint64 faces, + const QString& uuid) + : m_result(result) + , m_hasHighlight(isHighlighted) + , m_color(color) + , m_displayed(displayed) + , m_faces(faces) + , m_uuid(uuid) +{ +} + +HighLightDice::~HighLightDice() {} + +QList<qint64> HighLightDice::result() const +{ + return m_result; +} + +void HighLightDice::setResult(const QList<qint64>& result) +{ + m_result= result; +} + +bool HighLightDice::isHighlighted() const +{ + return m_hasHighlight; +} + +void HighLightDice::setHighlight(bool hasHighlight) +{ + m_hasHighlight= hasHighlight; +} + +QString HighLightDice::color() const +{ + return m_color; +} + +void HighLightDice::setColor(const QString& color) +{ + m_color= color; +} + +bool HighLightDice::displayed() const +{ + return m_displayed; +} + +void HighLightDice::setDisplayed(bool displayed) +{ + m_displayed= displayed; +} + +quint64 HighLightDice::faces() const +{ + return m_faces; +} + +void HighLightDice::setFaces(const quint64& faces) +{ + m_faces= faces; +} + +QString HighLightDice::getResultString() const +{ + if(m_result.size() == 1) + { + return QString::number(m_result.first()); + } + else + { + QStringList list; + std::transform(std::begin(m_result), std::end(m_result), std::back_inserter(list), + [](qint64 value) { return QString::number(value); }); + + auto totalScore= std::accumulate(std::begin(m_result), std::end(m_result), 0); + return QStringLiteral("%2 [%1]").arg(list.join(",")).arg(totalScore); + } +} + +QString HighLightDice::uuid() const +{ + return m_uuid; +} + +void HighLightDice::setUuid(const QString& uuid) +{ + m_uuid= uuid; +} diff --git a/src/libparser/include/diceparser/dicealias.h b/src/libparser/include/diceparser/dicealias.h new file mode 100644 index 0000000..05d5a0b --- /dev/null +++ b/src/libparser/include/diceparser/dicealias.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 DICEALIAS_H +#define DICEALIAS_H + +#include <QString> + +#include <diceparser/diceparser_global.h> +/** + * @brief The DiceAlias class is dedicated to store aliases, alias is mainly two QString. The Alias and its replacement. + * The replacement can be a simple QString or a RegExp. + */ +class DICEPARSER_EXPORT DiceAlias +{ +public: + enum RESOLUTION_TYPE + { + REPLACE, + REGEXP + }; + /** + * @brief DiceAlias + * @param cmd + * @param key + * @param isReplace + */ + DiceAlias(QString pattern, QString command, QString comment= QString{}, bool isReplace= true, bool isEnable= true); + DiceAlias(const DiceAlias& alias); + /** + * @brief ~DiceAlias + */ + virtual ~DiceAlias(); + /** + * @brief resolved + * @param str + * @return + */ + bool resolved(QString& str); + /** + * @brief setCommand + * @param key + */ + void setPattern(const QString& pattern); + /** + * @brief setValue + * @param value + */ + void setCommand(QString command); + /** + * @brief setType + */ + void setType(RESOLUTION_TYPE); + + /** + * @brief getCommand + * @return + */ + QString pattern() const; + /** + * @brief getValue + * @return + */ + QString command() const; + /** + * @brief isReplace + * @return + */ + bool isReplace() const; + /** + * @brief setReplace + */ + void setReplace(bool); + + /** + * @brief isEnable + * @return + */ + bool isEnable() const; + /** + * @brief setEnable + * @param b + */ + void setEnable(bool b); + /** + * @brief getComment + * @return + */ + QString comment() const; + /** + * @brief setComment + * @param comment + */ + void setComment(const QString& comment); + +private: + QString m_pattern; + QString m_command; + QString m_comment; + RESOLUTION_TYPE m_type; + bool m_isEnable; +}; + +#endif // DICEALIAS_H diff --git a/src/libparser/include/diceparser/diceparser.h b/src/libparser/include/diceparser/diceparser.h new file mode 100644 index 0000000..bf2d570 --- /dev/null +++ b/src/libparser/include/diceparser/diceparser.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 DICEPARSER_H +#define DICEPARSER_H + +#include <QJsonObject> +#include <QMap> +#include <QString> +#include <QVariant> +#include <memory> +#include <vector> + +#include <diceparser/diceparser_global.h> + +#include <diceparser/diceparserhelper.h> +#include <diceparser/highlightdice.h> + +class ExplodeDiceNode; +class ParsingToolBox; +class DiceRollerNode; +class DiceAlias; +class ExecutionNode; +/** + * @page DiceParser Dice Parser + * + * @section Intro Introduction + * Diceparser is the software component dedicated to compute dice command in rolisteam.<br/> + * + * @section grammar The Grammar + * + * The grammar is described in Readme.md + */ + +/** + * @brief The DiceParser class facade class, it receives a command and return a DiceResult class (not yet implemented). + */ +class DICEPARSER_EXPORT DiceParser +{ +public: + /** + * @brief DiceParser default constructor + */ + DiceParser(); + /** + * @brief ~DiceParser + */ + virtual ~DiceParser(); + + // Command process methods + /** + * @brief parseLine, method to call for starting the dice roll. It will parse the command and run the execution + * tree. + * @param str dice command + * @return bool every thing is fine or not + */ + bool parseLine(QString str, bool allowAlias= true); + void start(); + void cleanAll(); + + // debug + void writeDownDotTree(QString filepath); + + // control methods + bool hasIntegerResultNotInFirst() const; + bool hasDiceResult() const; + bool hasStringResult() const; + bool hasSeparator() const; + + // alias management + const QList<DiceAlias*>& constAliases() const; + QList<DiceAlias*>* aliases() const; + void cleanAliases(); + void insertAlias(DiceAlias*, int); + QString convertAlias(const QString& cmd) const; + + QStringList allFirstResultAsString(bool& hasAlias); + QStringList getAllDiceResult(bool& hasAlias); + + // Accessors + int startNodeCount() const; + QList<qreal> scalarResultsFromEachInstruction() const; + QStringList stringResultFromEachInstruction(bool& hasAlias) const; + void diceResultFromEachInstruction(QList<ExportedDiceResult>& resultList) const; + QString finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize) const; + + QString diceCommand() const; + QMap<Dice::ERROR_CODE, QString> errorMap() const; + QString comment() const; + QString humanReadableWarning() const; + QString humanReadableError() const; + QString resultAsJSon(std::function<QString(const QString&, const QString&, bool)> colorize, + bool removeUnhighligthed= false) const; + + // QStringList stringResult() const; + // QStringList allDiceResult(bool& hasAlias) const; + // void lastDiceResult(QList<ExportedDiceResult>& diceValues, bool& homogeneous) const; + + // setters + void setPathToHelp(QString l); + void setVariableDictionary(const QHash<QString, QString>& variables); + void setComment(const QString& comment); + +private: + bool readBlocInstruction(QString& str, ExecutionNode*& resultnode); + +private: + std::unique_ptr<ParsingToolBox> m_parsingToolbox; + QString m_command; +}; + +#endif // DICEPARSER_H diff --git a/src/libparser/include/diceparser/diceparser_global.h b/src/libparser/include/diceparser/diceparser_global.h new file mode 100644 index 0000000..47829cc --- /dev/null +++ b/src/libparser/include/diceparser/diceparser_global.h @@ -0,0 +1,11 @@ +#ifndef DICEPARSER_GLOBAL_H +#define DICEPARSER_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(DICEPARSER_LIBRARY) +#define DICEPARSER_EXPORT Q_DECL_EXPORT +#else +#define DICEPARSER_EXPORT Q_DECL_IMPORT +#endif +#endif // DICEPARSER_GLOBAL_H diff --git a/src/libparser/include/diceparser/diceparserhelper.h b/src/libparser/include/diceparser/diceparserhelper.h new file mode 100644 index 0000000..6190df2 --- /dev/null +++ b/src/libparser/include/diceparser/diceparserhelper.h @@ -0,0 +1,96 @@ +#ifndef DICEPARSERHELPER_H +#define DICEPARSERHELPER_H + +class ValidatorList; +class ExecutionNode; + +#include <diceparser/diceparser_global.h> + +namespace Dice +{ +enum class CONDITION_STATE : int +{ + ERROR_STATE, + ALWAYSTRUE, + UNREACHABLE, + REACHABLE +}; + +enum class ERROR_CODE : int +{ + NO_DICE_ERROR, + DIE_RESULT_EXPECTED, + BAD_SYNTAXE, + ENDLESS_LOOP_ERROR, + DIVIDE_BY_ZERO, + NOTHING_UNDERSTOOD, + NO_DICE_TO_ROLL, + TOO_MANY_DICE, + NO_VARIBALE, + INVALID_INDEX, + UNEXPECTED_CHARACTER, + NO_PREVIOUS_ERROR, + NO_VALID_RESULT, + SCALAR_RESULT_EXPECTED +}; + +/** + * @brief The RESULT_TYPE enum or combinaison + */ +enum class RESULT_TYPE : int +{ + NONE= 0, + SCALAR= 1, + STRING= 2, + DICE_LIST= 4 +}; +/** + * @brief The ConditionType enum defines compare method + */ +enum ConditionType +{ + OnEach, + OnEachValue, + OneOfThem, + AllOfThem, + OnScalar +}; + +enum class CompareOperator +{ + Equal, + GreaterThan, + LesserThan, + GreaterOrEqual, + LesserOrEqual, + Different +}; +enum class ArithmeticOperator +{ + PLUS, + MINUS, + DIVIDE, + MULTIPLICATION, + INTEGER_DIVIDE, + POW +}; +enum class LogicOperation +{ + OR, + EXCLUSIVE_OR, + AND, + NONE +}; + +enum class ConditionOperator +{ + Modulo +}; + +struct DICEPARSER_EXPORT CaseInfo +{ + ValidatorList* validatorList; + ExecutionNode* node; +}; +} // namespace Dice +#endif // DICEPARSERHELPER_H diff --git a/src/libparser/include/diceparser/highlightdice.h b/src/libparser/include/diceparser/highlightdice.h new file mode 100644 index 0000000..a3928a1 --- /dev/null +++ b/src/libparser/include/diceparser/highlightdice.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2016 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 HighLightDice_H +#define HighLightDice_H + +#include <QList> +#include <QMap> +#include <QString> + +#include <diceparser/diceparser_global.h> + +class DICEPARSER_EXPORT HighLightDice +{ +public: + HighLightDice(QList<qint64> result, bool isHighlighted, QString color, bool displayed, quint64 faces, + const QString& uuid); + virtual ~HighLightDice(); + + QList<qint64> result() const; + void setResult(const QList<qint64>& result); + + bool isHighlighted() const; + void setHighlight(bool hasHighlight); + + QString color() const; + void setColor(const QString& color); + + bool displayed() const; + void setDisplayed(bool displayed); + + quint64 faces() const; + void setFaces(const quint64& faces); + + QString getResultString() const; + + QString uuid() const; + void setUuid(const QString& uuid); + +private: + QList<qint64> m_result; + bool m_hasHighlight= true; + QString m_color; + bool m_displayed= false; + quint64 m_faces; + QString m_uuid; +}; + +typedef QList<HighLightDice> ListDiceResult; +typedef QMap<quint64, QList<ListDiceResult>> ExportedDiceResult; + +#endif // HighLightDice_H diff --git a/src/libparser/include/diceparser/parsingtoolbox.h b/src/libparser/include/diceparser/parsingtoolbox.h new file mode 100644 index 0000000..69f433a --- /dev/null +++ b/src/libparser/include/diceparser/parsingtoolbox.h @@ -0,0 +1,284 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 PARSINGTOOLBOX_H +#define PARSINGTOOLBOX_H + +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QMap> +#include <QVariant> +#include <functional> +#include <vector> + +#include "diceparserhelper.h" +#include "highlightdice.h" +//#include "dicerollernode.h" +//#include "executionnode.h" +//#include "node/ifnode.h" +//#include "node/paintnode.h" +//#include "node/scalaroperatornode.h" +//#include "operationcondition.h" +//#include "range.h" +//#include "validatorlist.h" + +#include <diceparser/diceparser_global.h> +class Range; +class RepeaterNode; +class DiceAlias; +class ExplodeDiceNode; +class SwitchCaseNode; +class ReplaceValueNode; +class PainterNode; +class ValidatorList; +class Validator; +class DiceRollerNode; +class ExecutionNode; +class DICEPARSER_EXPORT SubtituteInfo +{ +public: + SubtituteInfo(); + + bool isValid() const; + + int length() const; + void setLength(int length); + + int resultIndex() const; + void setResultIndex(int valueIndex); + + int position() const; + void setPosition(int position); + + int digitNumber() const; + void setDigitNumber(int digitNumber); + + int subIndex() const; + void setSubIndex(int subindex); + +private: + int m_length= 2; + int m_digitNumber= 0; + int m_resultIndex= -1; + int m_position= -1; + int m_subIndex= -1; +}; + +/** + * @brief The ParsingToolBox is gathering many useful methods for dice parsing. + * Its goal is to make the diceparser a bit lighter. + */ +class DICEPARSER_EXPORT ParsingToolBox +{ +public: + enum LIST_OPERATOR + { + NONE= 0x00, + UNIQUE= 0x01, + NOCOMMA= 0x02, + UniqueAndNoComma= 0x03 + }; + + enum Function + { + REPEAT + }; + enum OptionOperator + { + 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 + TransformOption // T + }; + enum DiceOperator + { + D, + L + }; + enum NodeAction + { + JumpBackward + }; + ParsingToolBox(); + ParsingToolBox(const ParsingToolBox& data); + virtual ~ParsingToolBox(); + void clearUp(); + + // Build execution tree + DiceRollerNode* getDiceRollerNode(ExecutionNode* previous); + DiceRollerNode* addRollDiceNode(qint64 faces, ExecutionNode*); + ExplodeDiceNode* addExplodeDiceNode(qint64 faces, ExecutionNode* previous); + Dice::CONDITION_STATE isValidValidator(ExecutionNode* previous, ValidatorList* val); + ExecutionNode* addSort(ExecutionNode* e, bool b); + + // parsing tools + static bool readAscending(QString& str); + static bool readStopAtFirst(QString& str); + bool readLogicOperator(QString& str, Dice::CompareOperator& op); + Validator* readValidator(QString& str, bool hasSquare= false); + ValidatorList* readValidatorList(QString& str); + static bool readNumber(QString& str, qint64& myNumber); + static bool readString(QString& str, QString& strresult); + static bool readVariable(QString& str, qint64& myNumber, QString& reasonFail); + static bool readOpenParentheses(QString& str); + static bool readCloseParentheses(QString& str); + + static bool readDynamicVariable(QString& str, qint64& index); + bool readList(QString& str, QStringList& list, QList<Range>& ranges); + bool readDiceRange(QString& str, qint64& start, qint64& end); + static LIST_OPERATOR readListOperator(QString& str); + void readProbability(QStringList& str, QList<Range>& ranges); + bool readLogicOperation(QString& str, Dice::LogicOperation& op); + bool readDiceLogicOperator(QString& str, Dice::ConditionOperator& op); + bool readArithmeticOperator(QString& str, Dice::ArithmeticOperator& op); + std::vector<ExecutionNode*> readInstructionList(QString& str, bool startNode); + static Dice::ConditionType readConditionType(QString& str); + bool readComment(QString& str, QString&, QString&); + bool readOperand(QString& str, ExecutionNode*& node); + static int findClosingCharacterIndexOf(QChar open, QChar closing, const QString& str, int offset); + static void readSubtitutionParameters(SubtituteInfo& info, QString& rest); + static bool readPainterParameter(PainterNode* painter, QString& str); + static bool readComma(QString& str); + bool readReaperArguments(RepeaterNode* node, QString& source); + bool readExpression(QString& str, ExecutionNode*& node); + bool readInstructionOperator(QChar c); + bool readNode(QString& str, ExecutionNode*& node); + /** + * @brief readIfInstruction reads the current command to build if node with proper parameters. + * @param str is the command string, if IF istruction is found, the str will be changed, in other case the string is + * unmodified + * @param trueNode is the branch's beginning to be executed if the IfNode is true. + * @param falseNode is the branch's beginning to be executed if the IfNode is false. + * @return true, ifNode has been found, false otherwise + */ + bool readIfInstruction(QString& str, ExecutionNode*& trueNode, ExecutionNode*& falseNode); + bool readOptionFromNull(QString& str, ExecutionNode*& node); + bool readOperatorFromNull(QString& str, ExecutionNode*& node); + bool readParameterNode(QString& str, ExecutionNode*& node); + bool readFunction(QString& str, ExecutionNode*& node); + bool readDice(QString& str, ExecutionNode*& node); + bool readDiceOperator(QString&, DiceOperator&); + bool readDiceExpression(QString&, ExecutionNode*& node); + bool readOperator(QString&, ExecutionNode* previous); + bool readCommand(QString& str, ExecutionNode*& node); + 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); + bool readReplaceValueNode(QString& str, ReplaceValueNode* node); + + // Error + bool hasError() const; + void addError(Dice::ERROR_CODE code, const QString& msg); + void addWarning(Dice::ERROR_CODE code, const QString& msg); + const QMap<Dice::ERROR_CODE, QString>& getErrorList() const; + const QMap<Dice::ERROR_CODE, QString>& getWarningList() const; + + // Traversal functions + static QString number(qreal value); + static ExecutionNode* getLeafNode(ExecutionNode* start); + const std::vector<ExecutionNode*>& getStartNodes(); + static void setStartNodes(std::vector<ExecutionNode*>* startNodes); + std::pair<bool, QVariant> hasResultOfType(Dice::RESULT_TYPE, ExecutionNode* node, bool notthelast= false) const; + QList<qreal> scalarResultsFromEachInstruction() const; + std::pair<QString, QString> finalScalarResult() const; + QString finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize, + bool removeUnhighlighted= false) const; + QStringList allFirstResultAsString(bool& hasAlias) const; + QList<qreal> sumOfDiceResult() const; + QList<ExportedDiceResult> diceResultFromEachInstruction() const; + bool hasIntegerResultNotInFirst() const; + bool hasDiceResult() const; + bool hasStringResult() const; + + // result + static QString replaceVariableToValue(const QString& source, QStringList values, + QMap<Dice::ERROR_CODE, QString>& errorMap); + static QString replacePlaceHolderToValue(const QString& source, const QList<ExportedDiceResult>& list, + bool removeUnhighlighted, + std::function<QString(const QString&, const QString&, bool)> colorize); + static SubtituteInfo readVariableFromString(const QString& source, int& start); + static SubtituteInfo readPlaceHolderFromString(const QString& source, int& start); + static ExportedDiceResult finalDiceResultFromInstruction(ExecutionNode* start); + static ExportedDiceResult allDiceResultFromInstruction(ExecutionNode* start); + void addResultInJson(QJsonObject& obj, Dice::RESULT_TYPE type, const QString& key, ExecutionNode* start, bool b); + void addDiceResultInJson(QJsonObject& obj, ExecutionNode* start, + std::function<QString(const QString&, const QString&, bool)> colorize); + + // accessors + void setComment(const QString& comment); + QString getComment() const; + void setHelpPath(const QString& path); + static QHash<QString, QString> getVariableHash(); + static void setVariableHash(const QHash<QString, QString>& variableHash); + void setStartNodes(std::vector<ExecutionNode*> nodes); + + // Aliases + QString convertAlias(QString str); + void insertAlias(DiceAlias* dice, int i); + const QList<DiceAlias*>& getAliases() const; + QList<DiceAlias*>* aliases(); + void setAliases(const QList<DiceAlias*> list); + void cleanUpAliases(); + + static bool readStringResultParameter(QString& str); + static QString replacePlaceHolderFromJson(const QString& source, const QJsonObject& obj); + +private: + QMap<QString, Dice::CompareOperator> m_logicOp; + QMap<QString, Dice::LogicOperation> m_logicOperation; + QMap<QString, Dice::ConditionOperator> m_conditionOperation; + std::vector<std::pair<QString, Dice::ArithmeticOperator>> m_arithmeticOperation; + QMap<QString, DiceOperator> m_mapDiceOp; + QMap<QString, OptionOperator> m_OptionOp; + QMap<QString, NodeAction> m_nodeActionMap; + std::map<QString, Function> m_functionMap; + QStringList m_commandList; + + QMap<Dice::ERROR_CODE, QString> m_errorMap; + QMap<Dice::ERROR_CODE, QString> m_warningMap; + std::vector<ExecutionNode*> m_startNodes; + + QString m_comment; + + static QHash<QString, QString> m_variableHash; + QString m_helpPath; + QList<DiceAlias*> m_aliasList; +}; + +#endif // PARSINGTOOLBOX_H diff --git a/src/libparser/include/diceparser_qobject/diceparser_qobject_global.h b/src/libparser/include/diceparser_qobject/diceparser_qobject_global.h new file mode 100644 index 0000000..ea49727 --- /dev/null +++ b/src/libparser/include/diceparser_qobject/diceparser_qobject_global.h @@ -0,0 +1,11 @@ +#ifndef DICEPARSER_QOBJECT_GLOBAL_H +#define DICEPARSER_QOBJECT_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(DICEPARSER_QOBJECT_LIBRARY) +#define DICEPARSER_QOBJECT_EXPORT Q_DECL_EXPORT +#else +#define DICEPARSER_QOBJECT_EXPORT Q_DECL_IMPORT +#endif +#endif // DICEPARSER_QOBJECT_GLOBAL_H diff --git a/src/libparser/include/diceparser_qobject/diceroller.h b/src/libparser/include/diceparser_qobject/diceroller.h new file mode 100644 index 0000000..72c4e10 --- /dev/null +++ b/src/libparser/include/diceparser_qobject/diceroller.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2017 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * Rolisteam 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 DICEROLLER_H +#define DICEROLLER_H + +#include <QObject> +#include <diceparser/diceparser.h> + +#include <diceparser_qobject/diceparser_qobject_global.h> + +class DICEPARSER_QOBJECT_EXPORT DiceRoller : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal result READ result NOTIFY resultChanged) + Q_PROPERTY(QString dicelist READ diceList NOTIFY diceListChanged) + Q_PROPERTY(QString resultStr READ resultStr NOTIFY resultStrChanged) + Q_PROPERTY(QString command READ command WRITE setCommand NOTIFY commandChanged) + Q_PROPERTY(QString error READ error WRITE setError NOTIFY errorOccurs) + +public: + DiceRoller(QObject* parent= nullptr); + + QString diceList() const; + QString resultStr() const; + QString command() const; + qreal result() const; + QString error() const; + QList<DiceAlias*>* aliases() const; + + DiceParser* parser(); + +signals: + void resultChanged(); + void diceListChanged(); + void resultStrChanged(); + void commandChanged(); + void errorOccurs(); + +public slots: + void start(); + void readErrorAndWarning(); + void setCommand(const QString& cmd); + void setError(const QString& error); + + /*protected: + QString diceToText(QList<ExportedDiceResult>& diceList);*/ + +private: + DiceParser m_diceparser; + qreal m_result; + QString m_diceList; + QString m_resultStr; + QString m_command; + QString m_error; +}; + +#endif // DICEROLLER_H diff --git a/src/libparser/include/diceparser_qobject/qmltypesregister.h b/src/libparser/include/diceparser_qobject/qmltypesregister.h new file mode 100644 index 0000000..e0b1a09 --- /dev/null +++ b/src/libparser/include/diceparser_qobject/qmltypesregister.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2017 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * 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 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 REGISTER_QML_TYPE_H +#define REGISTER_QML_TYPE_H + +#include <diceparser_qobject/diceparser_qobject_global.h> + +DICEPARSER_QOBJECT_EXPORT void registerQmlTypes(); + +#endif // REGISTER_QML_TYPE_H diff --git a/src/libparser/node/allsamenode.cpp b/src/libparser/node/allsamenode.cpp new file mode 100644 index 0000000..3b2f10f --- /dev/null +++ b/src/libparser/node/allsamenode.cpp @@ -0,0 +1,86 @@ +#include "allsamenode.h" + +AllSameNode::AllSameNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} + +void AllSameNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + DiceResult* previous_result= dynamic_cast<DiceResult*>(previous->getResult()); + if(nullptr != previous_result) + { + m_result->setPrevious(previous_result); + bool allSame= true; + int i= 0; + qint64 previousValue= 0; + if(previous_result->getResultList().size() < 2) + { + m_errors.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QStringLiteral("T operator must operate on more than 1 die")); + return; + } + for(auto& die : previous_result->getResultList()) + { + if(i == 0) + previousValue= die->getValue(); + Die* tmpdie= new Die(*die); + m_diceResult->insertResult(tmpdie); + die->displayed(); + if(previousValue != die->getValue()) + allSame= false; + ++i; + } + + while(allSame) + { + QList<Die*> list= m_diceResult->getResultList(); + qint64 pValue= 0; + int i= 0; + for(auto& die : list) + { + die->roll(true); + if(i == 0) + pValue= die->getValue(); + if(pValue != die->getValue()) + allSame= false; + ++i; + } + } + } + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString AllSameNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"AllSameNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 AllSameNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} + +ExecutionNode* AllSameNode::getCopy() const +{ + return new AllSameNode(); +} diff --git a/src/libparser/node/allsamenode.h b/src/libparser/node/allsamenode.h new file mode 100644 index 0000000..e5c1dc2 --- /dev/null +++ b/src/libparser/node/allsamenode.h @@ -0,0 +1,33 @@ +#ifndef ALLSAMENODE_H +#define ALLSAMENODE_H + +#include "executionnode.h" + +#include "result/diceresult.h" +#include "validator.h" + +class AllSameNode : public ExecutionNode +{ +public: + AllSameNode(); +// virtual ~AllSameNode(); + + virtual void run(ExecutionNode* previous); + /** + * @brief toString + * @return + */ + virtual QString toString(bool withLabel) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + + virtual ExecutionNode* getCopy() const; + +private: + DiceResult* m_diceResult; +}; + +#endif // FILTERNODE_H diff --git a/src/libparser/node/bind.cpp b/src/libparser/node/bind.cpp new file mode 100644 index 0000000..490071f --- /dev/null +++ b/src/libparser/node/bind.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "bind.h" + +BindNode::BindNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +void BindNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == m_previousNode) + return; + + m_result->setPrevious(previous->getResult()); + for(auto start : *m_startList) + { + ExecutionNode* last= getLatestNode(start); + if(nullptr != last) + { + auto tmpResult= last->getResult(); + while(nullptr != tmpResult) + { + DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult); + if(nullptr != dice) + { + m_diceResult->setHomogeneous(false); + for(auto& die : dice->getResultList()) + { + if(!die->hasBeenDisplayed()) + { + Die* tmpdie= new Die(*die); + die->displayed(); + m_diceResult->getResultList().append(tmpdie); + } + } + } + tmpResult= tmpResult->getPrevious(); + } + } + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +ExecutionNode* BindNode::getLatestNode(ExecutionNode* node) +{ + ExecutionNode* next= node; + while(nullptr != next->getNextNode() && (next->getNextNode() != this)) + { + next= next->getNextNode(); + } + return next; +} +QString BindNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"Bind Node\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 BindNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} +ExecutionNode* BindNode::getCopy() const +{ + BindNode* node= new BindNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +std::vector<ExecutionNode*>* BindNode::getStartList() const +{ + return m_startList; +} + +void BindNode::setStartList(std::vector<ExecutionNode*>* startList) +{ + m_startList= startList; +} diff --git a/src/libparser/node/bind.h b/src/libparser/node/bind.h new file mode 100644 index 0000000..71bbfb0 --- /dev/null +++ b/src/libparser/node/bind.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 BINDNODE_H +#define BINDNODE_H + +#include "node/executionnode.h" +#include "result/diceresult.h" + +/** + * @brief The MergeNode class is an ExecutionNode. It is dedicated to merge result of several commands. + */ +class BindNode : public ExecutionNode +{ +public: + BindNode(); + void run(ExecutionNode* previous); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + std::vector<ExecutionNode*>* getStartList() const; + void setStartList(std::vector<ExecutionNode*>* startList); + +private: + ExecutionNode* getLatestNode(ExecutionNode* node); + +private: + DiceResult* m_diceResult; + std::vector<ExecutionNode*>* m_startList; +}; + +#endif // NUMBERNODE_H diff --git a/src/libparser/node/countexecutenode.cpp b/src/libparser/node/countexecutenode.cpp new file mode 100644 index 0000000..b45fe37 --- /dev/null +++ b/src/libparser/node/countexecutenode.cpp @@ -0,0 +1,76 @@ +#include "countexecutenode.h" +#include "result/diceresult.h" +#include "validatorlist.h" + +CountExecuteNode::CountExecuteNode() : m_scalarResult(new ScalarResult()), m_validatorList(nullptr) +{ + m_result= m_scalarResult; +} +void CountExecuteNode::setValidatorList(ValidatorList* validatorlist) +{ + m_validatorList= validatorlist; +} +CountExecuteNode::~CountExecuteNode() +{ + if(nullptr != m_validatorList) + { + delete m_validatorList; + } +} + +void CountExecuteNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + m_errors.insert(Dice::ERROR_CODE::NO_PREVIOUS_ERROR, QStringLiteral("No scalar result before Swith/Case operator")); + return; + } + DiceResult* previousResult= dynamic_cast<DiceResult*>(previous->getResult()); + if(nullptr != previousResult) + { + m_result->setPrevious(previousResult); + qint64 sum= 0; + std::function<void(Die*, qint64)> f= [&sum](const Die*, qint64 score) { sum+= score; }; + m_validatorList->validResult(previousResult, true, true, f); + m_scalarResult->setValue(sum); + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } +} +QString CountExecuteNode::toString(bool withlabel) const +{ + if(withlabel) + { + return QString("%1 [label=\"CountExecuteNode %2\"]").arg(m_id, m_validatorList->toString()); + } + else + { + return m_id; + } +} +qint64 CountExecuteNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* CountExecuteNode::getCopy() const +{ + CountExecuteNode* node= new CountExecuteNode(); + if(nullptr != m_validatorList) + { + node->setValidatorList(m_validatorList->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/countexecutenode.h b/src/libparser/node/countexecutenode.h new file mode 100644 index 0000000..37b7a4f --- /dev/null +++ b/src/libparser/node/countexecutenode.h @@ -0,0 +1,50 @@ +#ifndef COUNTEXECUTENODE_H +#define COUNTEXECUTENODE_H + +#include "executionnode.h" + +#include "result/scalarresult.h" + +class ValidatorList; +/** + * @brief The CountExecuteNode class + */ +class CountExecuteNode : public ExecutionNode +{ +public: + /** + * @brief CountExecuteNode + */ + CountExecuteNode(); + virtual ~CountExecuteNode(); + /** + * @brief run + * @param previous + */ + virtual void run(ExecutionNode* previous); + /** + * @brief setValidator + */ + virtual void setValidatorList(ValidatorList*); + /** + * @brief toString + * @return + */ + virtual QString toString(bool withLabel) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + ScalarResult* m_scalarResult; + ValidatorList* m_validatorList; +}; + +#endif // COUNTEXECUTENODE_H diff --git a/src/libparser/node/dicerollernode.cpp b/src/libparser/node/dicerollernode.cpp new file mode 100644 index 0000000..2b00c0a --- /dev/null +++ b/src/libparser/node/dicerollernode.cpp @@ -0,0 +1,121 @@ +#include "dicerollernode.h" +#include "die.h" + +#include <QDebug> +#include <QThread> +#include <QThreadPool> +#include <QTime> + +DiceRollerNode::DiceRollerNode(qint64 max, qint64 min) + : m_max(max), m_diceResult(new DiceResult()), m_min(min), m_operator(Dice::ArithmeticOperator::PLUS) +{ + m_result= m_diceResult; +} +void DiceRollerNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + Result* result= previous->getResult(); + if(nullptr != result) + { + auto num= result->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + if(num <= 0) + { + m_errors.insert(Dice::ERROR_CODE::NO_DICE_TO_ROLL, QObject::tr("No dice to roll")); + } + m_diceCount= num > 0 ? static_cast<quint64>(num) : 0; + m_result->setPrevious(result); + + auto possibleValue= static_cast<quint64>(std::abs((m_max - m_min) + 1)); + if(possibleValue < m_diceCount && m_unique) + { + m_errors.insert(Dice::ERROR_CODE::TOO_MANY_DICE, + QObject::tr("More unique values asked than possible values (D operator)")); + return; + } + + for(quint64 i= 0; i < m_diceCount; ++i) + { + Die* die= new Die(); + die->setOp(m_operator); + die->setBase(m_min); + die->setMaxValue(m_max); + die->roll(); + if(m_unique) + { + const auto& equal= [](const Die* a, const Die* b) { return a->getValue() == b->getValue(); }; + while(m_diceResult->contains(die, equal)) + { + die->roll(false); + } + } + m_diceResult->insertResult(die); + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + } +} + +quint64 DiceRollerNode::getFaces() const +{ + return static_cast<quint64>(std::abs(m_max - m_min) + 1); +} + +std::pair<qint64, qint64> DiceRollerNode::getRange() const +{ + return std::make_pair(m_min, m_max); +} +QString DiceRollerNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"DiceRollerNode faces: %2\"]").arg(m_id).arg(getFaces()); + } + else + { + return m_id; + } +} +qint64 DiceRollerNode::getPriority() const +{ + qint64 priority= 4; + // if(nullptr!=m_nextNode) + // { + // priority = m_nextNode->getPriority(); + // } + return priority; +} +ExecutionNode* DiceRollerNode::getCopy() const +{ + DiceRollerNode* node= new DiceRollerNode(m_max, m_min); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +Dice::ArithmeticOperator DiceRollerNode::getOperator() const +{ + return m_operator; +} + +void DiceRollerNode::setOperator(const Dice::ArithmeticOperator& dieOperator) +{ + m_operator= dieOperator; + m_diceResult->setOperator(dieOperator); +} + +bool DiceRollerNode::getUnique() const +{ + return m_unique; +} + +void DiceRollerNode::setUnique(bool unique) +{ + m_unique= unique; +} diff --git a/src/libparser/node/dicerollernode.h b/src/libparser/node/dicerollernode.h new file mode 100644 index 0000000..52178cb --- /dev/null +++ b/src/libparser/node/dicerollernode.h @@ -0,0 +1,63 @@ +#ifndef DICEROLLERNODE_H +#define DICEROLLERNODE_H + +#include <Qt> + +#include "executionnode.h" +#include "result/diceresult.h" +#include <utility> +/** + * @brief The DiceRollerNode class rolls dice of one kind. + */ +class DiceRollerNode : public ExecutionNode +{ +public: + /** + * @brief DiceRollerNode builds an instance + * @param faces, number of faces of dices + * @param offset, first value of dice. + */ + DiceRollerNode(qint64 max, qint64 min= 1); + + /** + * @brief run - starts to roll dice. + */ + virtual void run(ExecutionNode*); + /** + * @brief getFaces accessor + * @return the face count + */ + quint64 getFaces() const; + std::pair<qint64, qint64> getRange() const; + + /** + * @brief toString + * @param wl + * @return use to generate dot tree; + */ + virtual QString toString(bool wl) const; + /** + * @brief getPriority + * @return priority for dice roll: 4 (higher) + */ + virtual qint64 getPriority() const; + + virtual ExecutionNode* getCopy() const; + + // private members + Dice::ArithmeticOperator getOperator() const; + void setOperator(const Dice::ArithmeticOperator& dieOperator); + + bool getUnique() const; + void setUnique(bool unique); + +private: + quint64 m_diceCount; + qint64 m_max; /// faces + DiceResult* m_diceResult{nullptr}; + qint64 m_min; + Dice::ArithmeticOperator m_operator; + bool m_unique{false}; +}; + +#endif // DICEROLLERNODE_H diff --git a/src/libparser/node/executionnode.cpp b/src/libparser/node/executionnode.cpp new file mode 100644 index 0000000..4545934 --- /dev/null +++ b/src/libparser/node/executionnode.cpp @@ -0,0 +1,101 @@ +#include "executionnode.h" + +#include <QUuid> + +ExecutionNode::ExecutionNode() + : m_previousNode(nullptr) + , m_result(nullptr) + , m_nextNode(nullptr) + , m_errors(QMap<Dice::ERROR_CODE, QString>()) + , m_id(QString("\"%1\"").arg(QUuid::createUuid().toString())) +{ +} +ExecutionNode::~ExecutionNode() +{ + if(nullptr != m_result) + { + delete m_result; + m_result= nullptr; + } + if(nullptr != m_nextNode) + { + delete m_nextNode; + m_nextNode= nullptr; + } +} + +Result* ExecutionNode::getResult() +{ + return m_result; +} +void ExecutionNode::setNextNode(ExecutionNode* node) +{ + m_nextNode= node; +} +void ExecutionNode::setPreviousNode(ExecutionNode* node) +{ + m_previousNode= node; +} +ExecutionNode* ExecutionNode::getNextNode() +{ + return m_nextNode; +} +QMap<Dice::ERROR_CODE, QString> ExecutionNode::getExecutionErrorMap() +{ + if(nullptr != m_nextNode) + { + auto const& keys= m_nextNode->getExecutionErrorMap().keys(); + for(auto& key : keys) + { + m_errors.insert(key, m_nextNode->getExecutionErrorMap().value(key)); + } + } + return m_errors; +} +QString ExecutionNode::getHelp() +{ + return QString(); +} +ExecutionNode* ExecutionNode::getPreviousNode() const +{ + return m_previousNode; +} +void ExecutionNode::generateDotTree(QString& s) +{ + auto str= toString(true); + if(s.contains(str)) + return; + s.append(toString(true)); + s.append(";\n"); + + if(nullptr != m_nextNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_nextNode->toString(false)); + s.append("[label=\"next\"];\n"); + // s.append(" [label=\"nextNode\"];\n"); + m_nextNode->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr;\n"); + } + if(nullptr != m_result) + { + s.append(toString(false)); + s.append(" ->"); + s.append(m_result->toString(false)); + s.append(" [label=\"Result\", style=\"dashed\"];\n"); + if(nullptr == m_nextNode) + m_result->generateDotTree(s); + } +} +qint64 ExecutionNode::getScalarResult() +{ + if(m_result == nullptr) + return 0; + return m_result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); +} diff --git a/src/libparser/node/executionnode.h b/src/libparser/node/executionnode.h new file mode 100644 index 0000000..d1bdf66 --- /dev/null +++ b/src/libparser/node/executionnode.h @@ -0,0 +1,102 @@ +#ifndef EXECUTIONNODE_H +#define EXECUTIONNODE_H + +#include "result/result.h" +#include <diceparser/diceparserhelper.h> + +/** + * @brief The ExecutionNode class + */ +class ExecutionNode +{ +public: + /** + * @brief ExecutionNode + */ + ExecutionNode(); + /** + * @brief ~ExecutionNode + */ + virtual ~ExecutionNode(); + /** + * @brief run + * @param previous + */ + virtual void run(ExecutionNode* previous= nullptr)= 0; + /** + * @brief getResult + * @return + */ + virtual Result* getResult(); + /** + * @brief setNextNode + */ + void setNextNode(ExecutionNode*); + /** + * @brief getNextNode + * @return + */ + ExecutionNode* getNextNode(); + /** + * @brief getPreviousNode + * @return + */ + virtual ExecutionNode* getPreviousNode() const; + void setPreviousNode(ExecutionNode* node); + /** + * @brief toString + * @return + */ + virtual QString toString(bool withLabel) const= 0; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const= 0; + /** + * @brief getErrorList + * @return + */ + virtual QMap<Dice::ERROR_CODE, QString> getExecutionErrorMap(); + + /** + * @brief generateDotTree + */ + virtual void generateDotTree(QString&); + + /** + * @brief getHelp + * @return + */ + virtual QString getHelp(); + + /** + * @brief getCopy + * @return should return a copy of that node. + */ + virtual ExecutionNode* getCopy() const= 0; + + virtual qint64 getScalarResult(); + +protected: + /** + * @brief m_nextNode + */ + ExecutionNode* m_previousNode= nullptr; + /** + * @brief m_result + */ + Result* m_result= nullptr; + /** + * @brief m_nextNode + */ + ExecutionNode* m_nextNode= nullptr; + /** + * @brief m_errors + */ + QMap<Dice::ERROR_CODE, QString> m_errors; + + QString m_id; +}; + +#endif // EXECUTIONNODE_H diff --git a/src/libparser/node/explodedicenode.cpp b/src/libparser/node/explodedicenode.cpp new file mode 100644 index 0000000..1546883 --- /dev/null +++ b/src/libparser/node/explodedicenode.cpp @@ -0,0 +1,117 @@ +#include "explodedicenode.h" +#include "validatorlist.h" + +ExplodeDiceNode::ExplodeDiceNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +void ExplodeDiceNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if((nullptr != previous) && (nullptr != previous->getResult())) + { + DiceResult* previous_result= dynamic_cast<DiceResult*>(previous->getResult()); + m_result->setPrevious(previous_result); + if(nullptr != previous_result) + { + Die* exampleDie; + for(auto& die : previous_result->getResultList()) + { + Die* tmpdie= new Die(*die); + m_diceResult->insertResult(tmpdie); + die->displayed(); + exampleDie= tmpdie; + } + + // QList<Die*> list= m_diceResult->getResultList(); + + bool hasExploded= false; + std::function<void(Die*, qint64)> f= [&hasExploded, this](Die* die, qint64) { + if(Dice::CONDITION_STATE::ALWAYSTRUE + == m_validatorList->isValidRangeSize( + std::make_pair<qint64, qint64>(die->getBase(), die->getMaxValue()))) + { + m_errors.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Condition (%1) cause an endless loop with this dice: %2") + .arg(toString(true)) + .arg(QStringLiteral("d[%1,%2]") + .arg(static_cast<int>(die->getBase())) + .arg(static_cast<int>(die->getMaxValue())))); + } + hasExploded= true; + die->roll(true); + }; + do + { + hasExploded= false; + m_validatorList->validResult(m_diceResult, false, false, f); + } while(hasExploded); + + /*for(auto& die : list) + { + if(Dice::CONDITION_STATE::ALWAYSTRUE + == m_validatorList->isValidRangeSize( + std::make_pair<qint64, qint64>(die->getBase(), die->getMaxValue()))) + { + + continue; + } + + while(m_validatorList->hasValid(die, false)) + { + die->roll(true); + } + }*/ + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + } +} +ExplodeDiceNode::~ExplodeDiceNode() +{ + if(nullptr != m_validatorList) + { + delete m_validatorList; + } +} +void ExplodeDiceNode::setValidatorList(ValidatorList* val) +{ + m_validatorList= val; +} +QString ExplodeDiceNode::toString(bool withlabel) const +{ + if(withlabel) + { + return QString("%1 [label=\"ExplodeDiceNode %2\"]").arg(m_id, m_validatorList->toString()); + } + else + { + return m_id; + } +} +qint64 ExplodeDiceNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* ExplodeDiceNode::getCopy() const +{ + ExplodeDiceNode* node= new ExplodeDiceNode(); + if(nullptr != m_validatorList) + { + node->setValidatorList(m_validatorList->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/explodedicenode.h b/src/libparser/node/explodedicenode.h new file mode 100644 index 0000000..77b4a44 --- /dev/null +++ b/src/libparser/node/explodedicenode.h @@ -0,0 +1,29 @@ +#ifndef EXPLOSEDICENODE_H +#define EXPLOSEDICENODE_H + +#include "executionnode.h" +#include "result/diceresult.h" + +class ValidatorList; + +/** + * @brief The ExplodeDiceNode class explode dice while is valid by the validator. + */ +class ExplodeDiceNode : public ExecutionNode +{ +public: + ExplodeDiceNode(); + virtual ~ExplodeDiceNode(); + virtual void run(ExecutionNode* previous= nullptr); + virtual void setValidatorList(ValidatorList*); + virtual QString toString(bool) const; + virtual qint64 getPriority() const; + + virtual ExecutionNode* getCopy() const; + +protected: + DiceResult* m_diceResult; + ValidatorList* m_validatorList= nullptr; +}; + +#endif // EXPLOSEDICENODE_H diff --git a/src/libparser/node/filternode.cpp b/src/libparser/node/filternode.cpp new file mode 100644 index 0000000..b72b6e1 --- /dev/null +++ b/src/libparser/node/filternode.cpp @@ -0,0 +1,94 @@ +#include "filternode.h" +#include "validatorlist.h" + +FilterNode::FilterNode() : m_diceResult(new DiceResult()), m_eachValue(false) +{ + m_result= m_diceResult; +} + +FilterNode::~FilterNode() +{ + if(nullptr != m_validatorList) + { + delete m_validatorList; + } +} +void FilterNode::setValidatorList(ValidatorList* validatorlist) +{ + m_validatorList= validatorlist; +} +void FilterNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + return; + } + DiceResult* previousDiceResult= dynamic_cast<DiceResult*>(previous->getResult()); + m_result->setPrevious(previousDiceResult); + + if(nullptr != previousDiceResult) + { + QList<Die*> diceList2; + std::function<void(Die*, qint64)> f= [&diceList2](Die* die, qint64) { + if(die == nullptr) + return; + Die* tmpdie= new Die(*die); + diceList2.append(tmpdie); + die->displayed(); + }; + m_validatorList->validResult(previousDiceResult, true, true, f); + + QList<Die*> diceList= previousDiceResult->getResultList(); + + diceList.erase(std::remove_if(diceList.begin(), diceList.end(), + [&diceList2](Die* die) { return diceList2.contains(die); }), + diceList.end()); + for(Die* tmp : diceList) + { + tmp->setHighlighted(false); + tmp->setDisplayed(true); + } + + m_diceResult->setResultList(diceList2); + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } +} + +QString FilterNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"FilterNode\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 FilterNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + + return priority; +} +ExecutionNode* FilterNode::getCopy() const +{ + FilterNode* node= new FilterNode(); + if(nullptr != m_validatorList) + { + node->setValidatorList(m_validatorList->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/filternode.h b/src/libparser/node/filternode.h new file mode 100644 index 0000000..7af6fe2 --- /dev/null +++ b/src/libparser/node/filternode.h @@ -0,0 +1,40 @@ +#ifndef FILTERNODE_H +#define FILTERNODE_H + +#include "executionnode.h" + +#include "result/diceresult.h" + +class ValidatorList; + +class FilterNode : public ExecutionNode +{ +public: + FilterNode(); + virtual ~FilterNode(); + + virtual void run(ExecutionNode* previous); + /** + * @brief setValidator + */ + virtual void setValidatorList(ValidatorList*); + /** + * @brief toString + * @return + */ + virtual QString toString(bool withLabel) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + + virtual ExecutionNode* getCopy() const; + +private: + DiceResult* m_diceResult; + ValidatorList* m_validatorList; + bool m_eachValue; +}; + +#endif // FILTERNODE_H diff --git a/src/libparser/node/forloopnode.cpp b/src/libparser/node/forloopnode.cpp new file mode 100644 index 0000000..aba6ff4 --- /dev/null +++ b/src/libparser/node/forloopnode.cpp @@ -0,0 +1,103 @@ +#include "forloopnode.h" + +#include "die.h" + +MockNode::MockNode() {} + +void MockNode::run(ExecutionNode* node) +{ + return; +} + +void MockNode::setResult(Result* result) +{ + m_result= result; +} + +QString MockNode::toString(bool) const +{ + return {}; +}; +qint64 MockNode::getPriority() const +{ + return 0; +} +ExecutionNode* MockNode::getCopy() const +{ + return new MockNode(); +} +// end mocknode + +ForLoopNode::ForLoopNode() : m_diceResult(new DiceResult) {} + +void ForLoopNode::setInternal(ExecutionNode* node) +{ + m_internal.reset(node); +} + +void ForLoopNode::run(ExecutionNode* previous) +{ + if(nullptr != previous) + { + auto prevResult= dynamic_cast<DiceResult*>(previous->getResult()); + if(nullptr != prevResult) + { + m_diceResult->setPrevious(prevResult); + QList<Die*> diceList= prevResult->getResultList(); + for(Die* dice : diceList) + { + MockNode node; + DiceResult diceResult; + diceResult.insertResult(dice); + node.setResult(&diceResult); + m_internal->run(&node); + + auto tmp= m_internal.get(); + while(nullptr != tmp->getNextNode()) + { + tmp= tmp->getNextNode(); + } + Result* internalResult= tmp->getResult(); + auto value= internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + + Die* neodie= new Die(); + *neodie= *dice; + neodie->setValue(value); + m_diceResult->insertResult(neodie); + node.setResult(nullptr); + diceResult.clear(); + dice->displayed(); + } + } + } + m_result= m_diceResult; + if(m_nextNode != nullptr) + m_nextNode->run(this); +} + +qint64 ForLoopNode::getPriority() const +{ + return 2; +} + +QString ForLoopNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"ForLoopNode Node\"]").arg(m_id); + } + else + { + return m_id; + } +} + +ExecutionNode* ForLoopNode::getCopy() const +{ + auto node= new ForLoopNode(); + if(m_internal) + { + node->setInternal(m_internal->getCopy()); + } + return node; +} diff --git a/src/libparser/node/forloopnode.h b/src/libparser/node/forloopnode.h new file mode 100644 index 0000000..a9acf20 --- /dev/null +++ b/src/libparser/node/forloopnode.h @@ -0,0 +1,36 @@ +#ifndef FORLOOPNODE_H +#define FORLOOPNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" +#include <memory> + +class MockNode : public ExecutionNode +{ +public: + MockNode(); + void run(ExecutionNode* node); + void setResult(Result* result); + QString toString(bool withLabel) const; + qint64 getPriority() const; + ExecutionNode* getCopy() const; +}; + +class ForLoopNode : public ExecutionNode +{ +public: + ForLoopNode(); + void run(ExecutionNode* previous); + + void setInternal(ExecutionNode* internal); + + QString toString(bool withLabel) const; + qint64 getPriority() const; + ExecutionNode* getCopy() const; + +private: + std::unique_ptr<ExecutionNode> m_internal; + DiceResult* m_diceResult; +}; + +#endif // FORLOOPNODE_H diff --git a/src/libparser/node/groupnode.cpp b/src/libparser/node/groupnode.cpp new file mode 100644 index 0000000..860d758 --- /dev/null +++ b/src/libparser/node/groupnode.cpp @@ -0,0 +1,312 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "groupnode.h" +#include "result/diceresult.h" +//------------------------------- +int DieGroup::getSum() const +{ + int sum= 0; + for(int i : *this) + { + sum+= i; + } + return sum; +} + +void DieGroup::removeValue(DieGroup i) +{ + for(auto x : i) + { + removeOne(x); + } +} + +int DieGroup::getLost() const +{ + return getSum() - m_exceptedValue; +} + +qint64 DieGroup::getExceptedValue() const +{ + return m_exceptedValue; +} + +void DieGroup::setExceptedValue(qint64 exceptedValue) +{ + m_exceptedValue= exceptedValue; +} + +//--------------------- +GroupNode::GroupNode(bool complexOutput) + : m_scalarResult(new ScalarResult), m_stringResult(new StringResult), m_complexOutput(complexOutput) +{ +} + +void GroupNode::run(ExecutionNode* previous) +{ + if(m_complexOutput) + m_result= m_stringResult; + else + m_result= m_scalarResult; + + m_previousNode= previous; + if(nullptr != previous) + { + m_result->setPrevious(previous->getResult()); + Result* tmpResult= previous->getResult(); + if(nullptr != tmpResult) + { + DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult); + if(nullptr != dice) + { + auto list= dice->getResultList(); + DieGroup allResult; + for(auto& die : list) + { + allResult << die->getListValue(); + } + std::sort(allResult.begin(), allResult.end(), std::greater<qint64>()); + if(allResult.getSum() > m_groupValue) + { + auto copy= allResult; + auto const die= getGroup(allResult); + + for(auto list : die) + { + for(auto val : list) + { + copy.removeOne(val); + } + } + m_scalarResult->setValue(die.size()); + QStringList list; + for(auto group : die) + { + QStringList values; + std::transform(group.begin(), group.end(), std::back_inserter(values), + [](qint64 val) { return QString::number(val); }); + list << QStringLiteral("{%1}").arg(values.join(",")); + } + QStringList unused; + std::transform(copy.begin(), copy.end(), std::back_inserter(unused), + [](qint64 val) { return QString::number(val); }); + if(!unused.isEmpty()) + m_stringResult->addText( + QStringLiteral("%1 (%2 - [%3])").arg(die.size()).arg(list.join(",")).arg(unused.join(","))); + else + m_stringResult->addText(QStringLiteral("%1 (%2)").arg(die.size()).arg(list.join(","))); + } + else + { + m_scalarResult->setValue(0); + } + } + } + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString GroupNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"SplitNode Node\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 GroupNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* GroupNode::getCopy() const +{ + GroupNode* node= new GroupNode(m_complexOutput); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +int GroupNode::getGroupValue() const +{ + return m_groupValue; +} + +void GroupNode::setGroupValue(qint64 groupValue) +{ + m_groupValue= groupValue; +} + +bool GroupNode::composeWithPrevious(DieGroup previous, qint64 first, qint64 current, DieGroup& addValue) +{ + if(previous.getSum() + first + current == m_groupValue) + { + addValue.append(previous); + addValue.append(first); + addValue.append(current); + return true; + } + + if(previous.isEmpty()) + return false; + + int maxComboLength= previous.size(); + bool hasReachMax= false; + + QList<DieGroup> possibleUnion; + for(auto va : previous) + { + DieGroup dieG; + dieG.append(va); + possibleUnion.append(dieG); + } + + while(!hasReachMax) + { + auto tmpValues= previous; + QList<DieGroup> possibleTmp; + for(auto& diaG : possibleUnion) + { + if(tmpValues.isEmpty()) + break; + tmpValues.removeValue(diaG); + + for(auto& value : tmpValues) + { + DieGroup dia; + dia.append(diaG); + dia.append(value); + if(dia.size() >= maxComboLength - 1) + hasReachMax= true; + else + possibleTmp.append(dia); + } + } + if(possibleTmp.isEmpty()) + hasReachMax= true; + else + { + possibleTmp.append(possibleUnion); + possibleUnion= possibleTmp; + } + } + std::sort(possibleUnion.begin(), possibleUnion.end(), + [=](const DieGroup& a, const DieGroup& b) { return a.getLost() > b.getLost(); }); + bool found= false; + for(int i= 0; (!found && i < possibleUnion.size()); ++i) + { + auto& value= possibleUnion.at(i); + if(value.getSum() + current + first >= m_groupValue) + { + addValue << value << current << first; + found= true; + } + } + return found; +} + +QList<DieGroup> GroupNode::getGroup(DieGroup values) +{ + if(values.isEmpty()) + return {}; + + auto first= values.takeFirst(); + + QList<DieGroup> result; + QMap<qint64, DieGroup> loseMap; + if(first >= m_groupValue) + { + DieGroup group; + group << first; + loseMap[0]= group; + } + else + { + auto it= values.rbegin(); + bool foundPerfect= false; + qint64 cumuledValue= 0; + DieGroup previousValue; + while((values.rend() != it) && !foundPerfect) + { + if(first + *it == m_groupValue) + { + foundPerfect= true; + DieGroup group; + group << first << *it; + loseMap[0]= group; + } + else if(first + *it > m_groupValue) + { + DieGroup group; + group << first << *it; + loseMap[first + *it - m_groupValue]= group; + } + else if(first + *it + cumuledValue == m_groupValue) + { + DieGroup group; + group << first << *it << previousValue; + foundPerfect= true; + loseMap[0]= group; + } + else if(first + *it + cumuledValue > m_groupValue) + { + DieGroup group; + group.setExceptedValue(m_groupValue); + auto b= composeWithPrevious(previousValue, first, *it, group); + if(b) + loseMap[group.getLost()]= group; + } + previousValue << *it; + cumuledValue+= *it; + ++it; + } + } + if(!loseMap.isEmpty()) + { + DieGroup die= loseMap.first(); + result.append(die); + DieGroup valueToRemove= die; + if(!valueToRemove.isEmpty()) + { + valueToRemove.removeFirst(); + values.removeValue(valueToRemove); + + if(values.getSum() >= m_groupValue) + { + result.append(getGroup(values)); + } + } + } + return result; +} diff --git a/src/libparser/node/groupnode.h b/src/libparser/node/groupnode.h new file mode 100644 index 0000000..d037080 --- /dev/null +++ b/src/libparser/node/groupnode.h @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 GROUPNODE_H +#define GROUPNODE_H + +#include "node/executionnode.h" +#include "result/scalarresult.h" +#include "result/stringresult.h" +#include <QList> + +class DieGroup : public QList<qint64> +{ +public: + int getSum() const; + void removeValue(DieGroup); + + int getLost() const; + + qint64 getExceptedValue() const; + void setExceptedValue(qint64 exceptedValue); + +private: + qint64 m_exceptedValue= 0; +}; +/** + * @brief The GroupNode class is an ExecutionNode. + */ +class GroupNode : public ExecutionNode +{ +public: + GroupNode(bool complexOutput= false); + void run(ExecutionNode* previous) override; + virtual QString toString(bool withLabel) const override; + virtual qint64 getPriority() const override; + virtual ExecutionNode* getCopy() const override; + + int getGroupValue() const; + void setGroupValue(qint64 groupValue); + + QList<DieGroup> getGroup(DieGroup); + +protected: + bool composeWithPrevious(DieGroup previous, qint64 first, qint64 current, DieGroup& addValue); + +private: + ScalarResult* m_scalarResult; + StringResult* m_stringResult; + qint64 m_groupValue; + QList<DieGroup> m_groupsList; + bool m_complexOutput= false; +}; + +#endif // GROUPNODE_H diff --git a/src/libparser/node/helpnode.cpp b/src/libparser/node/helpnode.cpp new file mode 100644 index 0000000..8ab48a9 --- /dev/null +++ b/src/libparser/node/helpnode.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 "helpnode.h" + +HelpNode::HelpNode() : m_path("https://github.com/Rolisteam/DiceParser/blob/master/HelpMe.md") +{ + m_result= new StringResult(); +} +void HelpNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + StringResult* txtResult= dynamic_cast<StringResult*>(m_result); + txtResult->setHighLight(false); + + if((nullptr == previous) && (txtResult != nullptr)) + { + txtResult->addText(QObject::tr("Rolisteam Dice Parser:\n" + "\n" + "Example (with ! as prefix):\n" + "!2d6\n" + "!1d20\n" + "!6d10e10k3 (L5R)\n" + "\n" + "Full documentation at: %1") + .arg(m_path)); + /*txtResult->setText(QObject::tr("Rolisteam Dice Parser:\n" + "\n" + "Example (with ! as prefix):\n" + "!2d6\n" + "!1d20\n" + "\n" + "Operator list:\n" + "\n" + "k : Keep : 2d10k1 => roll two 10-sided dice and keep the + higher one (kl1 for smaller one)\n" "K : Keep And Explode : 2d10K1 => Equivalent of 2d10e10k1\n" "s : + Sort : 8d10 => roll eight 10-sided dice and sort the result list\n" "c : Count : + 8d10c[>7] => roll eight 10-sided dice and count how many dice are higher than 7\n" "r : Reroll : + 8d6r1 => roll eight 6-sided dice and reroll dice once if its result is 1. (result of the reroll can be + 1)\n" "e : Explode : 8d10e10 => roll eight 10-sided dice and while dice makes a 10 it is + reroll. The result is added to those dice.\n" "a : Reroll and add : 8d6a1 => roll eight 6-sided dice + and reroll dice once and the result is added at 1\n" "m : Merge : 1d20;1d10mk1 => roll one 20-side + die and one 10-sided die and keep the higher die\n" "i : if : 1d100i[=100]{\"jackpot\"} => Roll + one 100-side die and display \"jackpot\" if the die result is 100.\n" "f : filter : 4d10f[!=4] => + roll four 10-sided dice and ignore all dice with 4 as result" + "; : Next instruction : 1d20;2d10;3d8 => roll one 20-sided die, two 10-sided + dice and three 8-sided dice \n" "g : Group : 8d10g10 => count how many group of 10 it is + possible to do (according to rule of 7th sea).\n" + "# : Comment : 1d2 #flip coin=> display flip coin as comment of 1 or 2 + result.\n" + "\n" + "Validator:\n" + "\n" + "Supported conditions: >,<,=,>=,<=,!=\n" + "Supported operators: % (modulo), &,^,| \n" + "\n" + " Examples:\n" + "\n" + "c[>7 & %2=0] : count how many dice are higher than 7 and even\n" + "c[>7 | %2=0] : count how many dice are higher than 7 or even\n" + "c[>7 ^ %2=0] : count how many dice are higher than 7 or even but not higher + than 7 and even\n" + "\n" + "List:\n" + "\n" + "1L[green,blue] => pick value from the list (green or blue)\n" + "2L[green,blue] => pick two values from the list (green,green | green,blue | + blue,green | blue,blue)\n" "2Lu[green,blue] => pick two unique values from the list (green,blue | + blue,green)\n" + "\n" + "Arithmetic\n" + "\n" + "8+8+8 => 24\n" + "24-4 => 20\n" + "(3+4)*2 => 14\n" + "7/2 => 3.5\n" + "2^4 => 16\n" + "1d6+6 => roll one 6-sided die and add 6 to its result\n" + "(2d4+2)d10 => roll two 4-sided dice, add 2 to the result[2;8] then roll from + four to ten 10-sided dice\n" + "\n" + "Full documentation at: %1").arg(m_path));*/ + m_result->setPrevious(nullptr); + } + else if(nullptr != previous) + { + txtResult->addText(previous->getHelp()); + m_result->setPrevious(previous->getResult()); + } + txtResult->finished(); + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +QString HelpNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"Rolisteam Dice Parser:\nFull documentation at: %2\"]").arg(m_id, m_path); + } + else + { + return m_id; + } +} + +qint64 HelpNode::getPriority() const +{ + return 0; +} +void HelpNode::setHelpPath(QString path) +{ + m_path= path; +} + +ExecutionNode* HelpNode::getCopy() const +{ + HelpNode* node= new HelpNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/helpnode.h b/src/libparser/node/helpnode.h new file mode 100644 index 0000000..a333c6d --- /dev/null +++ b/src/libparser/node/helpnode.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 HELPNODE_H +#define HELPNODE_H +#include "executionnode.h" + +#include <QObject> +#include <QString> + +#include "result/stringresult.h" + +/** + * @brief The HelpNode class + */ +class HelpNode : public ExecutionNode +{ +public: + /** + * @brief HelpNode + */ + HelpNode(); + /** + * @brief run + * @param previous + */ + void run(ExecutionNode* previous); + /** + * @brief toString + * @return + */ + virtual QString toString(bool) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief setHelpPath + * @param path + */ + void setHelpPath(QString path); + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + QString m_path; +}; + +#endif // HELPNODE_H diff --git a/src/libparser/node/ifnode.cpp b/src/libparser/node/ifnode.cpp new file mode 100644 index 0000000..8de3cd5 --- /dev/null +++ b/src/libparser/node/ifnode.cpp @@ -0,0 +1,354 @@ +/*************************************************************************** + * Copyright (C) 2016 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 "ifnode.h" +#include "result/diceresult.h" +#include "validatorlist.h" + +PartialDiceRollNode::PartialDiceRollNode() : m_diceResult(new DiceResult) +{ + m_result= m_diceResult; +} + +void PartialDiceRollNode::insertDie(Die* die) +{ + m_diceResult->insertResult(die); +} + +void PartialDiceRollNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + Result* presult= previous->getResult(); + if(nullptr != presult) + { + m_result->setPrevious(presult); + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +ExecutionNode* PartialDiceRollNode::getCopy() const +{ + return new PartialDiceRollNode(); +} +qint64 PartialDiceRollNode::getPriority() const +{ + qint64 priority= 4; + return priority; +} + +QString PartialDiceRollNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"PartialDiceRollNode \"]").arg(m_id); + } + else + { + return m_id; + } +} + +DiceResult* getFirstDiceResult(Result* result) +{ + DiceResult* found= nullptr; + + if(nullptr == result) + return found; + do + { + found= dynamic_cast<DiceResult*>(result); + result= result->getPrevious(); + } while((nullptr == found) && (result != nullptr)); + + return found; +} + +IfNode::IfNode() : m_conditionType(Dice::AllOfThem), m_true(nullptr), m_false(nullptr) +{ + // m_result = new DiceResult(); +} + +IfNode::~IfNode() +{ + m_result= nullptr; +} + +void IfNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + return; + } + ExecutionNode* previousLoop= previous; + ExecutionNode* nextNode= nullptr; + bool runNext= (nullptr == m_nextNode) ? false : true; + Result* previousResult= previous->getResult(); + m_result= previousResult->getCopy(); + + if(nullptr != m_result) + { + qreal value= previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + + if(nullptr != m_validatorList) + { + DiceResult* previousDiceResult= getFirstDiceResult(previousResult); + if(nullptr != previousDiceResult) + { + QList<Die*> diceList= previousDiceResult->getResultList(); + + if(m_conditionType == Dice::OnEach) + { + for(Die* dice : diceList) + { + auto diceNode= new PartialDiceRollNode(); + diceNode->insertDie(new Die(*dice)); + if(m_validatorList->hasValid(dice, true, true)) + { + nextNode= (nullptr == m_true) ? nullptr : m_true->getCopy(); + } + else + { + nextNode= (nullptr == m_false) ? nullptr : m_false->getCopy(); + } + + if(nullptr != nextNode) + { + if(nullptr == previousLoop->getNextNode()) + { + previousLoop->setNextNode(nextNode); + } + if(nullptr == m_nextNode) + { + m_nextNode= nextNode; + } + diceNode->setNextNode(nextNode); + diceNode->run(previousLoop); + previousLoop= getLeafNode(nextNode); + } + } + } + else if((m_conditionType == Dice::OneOfThem) || (m_conditionType == Dice::AllOfThem)) + { + bool trueForAll= true; + bool falseForAll= true; + + bool oneIsTrue= false; + bool oneIsFalse= false; + + for(Die* dice : diceList) + { + bool result= m_validatorList->hasValid(dice, true, true); + trueForAll= trueForAll ? result : false; + falseForAll= falseForAll ? result : false; + + oneIsTrue|= result; + oneIsFalse= !result ? true : oneIsFalse; + } + if(m_conditionType == Dice::OneOfThem) + { + if(oneIsTrue) + { + nextNode= (nullptr == m_true) ? nullptr : m_true->getCopy(); + } + else // if(oneIsFalse) + { + nextNode= (nullptr == m_false) ? nullptr : m_false->getCopy(); + } + } + else if(m_conditionType == Dice::AllOfThem) + { + if(trueForAll) + { + nextNode= (nullptr == m_true) ? nullptr : m_true->getCopy(); + } + else // if(falseForAll) + { + nextNode= (nullptr == m_false) ? nullptr : m_false->getCopy(); + } + } + + if(nullptr != nextNode) + { + if(nullptr == m_nextNode) + { + m_nextNode= nextNode; + } + nextNode->run(previousLoop); + previousLoop= getLeafNode(nextNode); + } + } + } + + if(m_conditionType == Dice::OnScalar) + { + Die dice; + auto val= static_cast<qint64>(value); + dice.setValue(val); + dice.insertRollValue(val); + dice.setMaxValue(val); + if(m_validatorList->hasValid(&dice, true, true)) + { + nextNode= m_true; + } + else + { + nextNode= m_false; + } + if(nullptr != nextNode) + { + if(nullptr == m_nextNode) + { + m_nextNode= nextNode; + } + nextNode->run(previousLoop); + previousLoop= getLeafNode(nextNode); + } + } + } + } + + if((nullptr != m_nextNode) && (runNext)) + { + m_nextNode->run(previousLoop); + } +} + +void IfNode::setValidatorList(ValidatorList* val) +{ + m_validatorList= val; +} +void IfNode::setInstructionTrue(ExecutionNode* node) +{ + m_true= node; +} + +void IfNode::setInstructionFalse(ExecutionNode* node) +{ + m_false= node; +} +void IfNode::generateDotTree(QString& s) +{ + s.append(toString(true)); + s.append(";\n"); + + if((nullptr != m_true) && (m_true != m_nextNode)) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_true->toString(false)); + s.append("[label=\"true" + m_validatorList->toString() + "\"];\n"); + + m_true->generateDotTree(s); + } + if((nullptr != m_false) && (m_false != m_nextNode)) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_false->toString(false)); + s.append("[label=\"false\"];\n"); + m_false->generateDotTree(s); + } + + if(nullptr != m_nextNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_nextNode->toString(false)); + s.append("[label=\"next\"];\n"); + m_nextNode->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr;\n"); + + if(nullptr != m_result) + { + s.append(toString(false)); + s.append(" ->"); + s.append(m_result->toString(false)); + s.append(" [label=\"Result\"];\n"); + m_result->generateDotTree(s); + } + } +} + +QString IfNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"IfNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 IfNode::getPriority() const +{ + return 0; +} + +ExecutionNode* IfNode::getLeafNode(ExecutionNode* node) +{ + ExecutionNode* next= node; + while(nullptr != next->getNextNode()) + { + next= next->getNextNode(); + } + return next; +} + +Dice::ConditionType IfNode::getConditionType() const +{ + return m_conditionType; +} + +void IfNode::setConditionType(const Dice::ConditionType& conditionType) +{ + m_conditionType= conditionType; +} +ExecutionNode* IfNode::getCopy() const +{ + IfNode* node= new IfNode(); + + node->setConditionType(m_conditionType); + if(nullptr != m_validatorList) + { + node->setValidatorList(m_validatorList->getCopy()); + } + if(nullptr != m_false) + { + node->setInstructionFalse(m_false->getCopy()); + } + if(nullptr != m_true) + { + node->setInstructionTrue(m_true->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/ifnode.h b/src/libparser/node/ifnode.h new file mode 100644 index 0000000..912d65c --- /dev/null +++ b/src/libparser/node/ifnode.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 IFNODE_H +#define IFNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" +#include <diceparser/diceparserhelper.h> + +class ValidatorList; +class PartialDiceRollNode : public ExecutionNode +{ +public: + PartialDiceRollNode(); + + void insertDie(Die* die); + virtual void run(ExecutionNode* previous= nullptr) override; + virtual qint64 getPriority() const override; + virtual ExecutionNode* getCopy() const override; + virtual QString toString(bool withLabel) const override; + +private: + DiceResult* m_diceResult; +}; + +/** + * @brief The ifNode class explode dice while is valid by the validator. + */ +class IfNode : public ExecutionNode +{ +public: + /** + * @brief IfNode + */ + IfNode(); + /** + * @brief ~IfNode + */ + virtual ~IfNode(); + /** + * @brief run + * @param previous + */ + virtual void run(ExecutionNode* previous= nullptr); + /** + * @brief setValidator + */ + virtual void setValidatorList(ValidatorList*); + /** + * @brief setInstructionTrue + */ + virtual void setInstructionTrue(ExecutionNode*); + /** + * @brief setInstructionFalse + */ + virtual void setInstructionFalse(ExecutionNode*); + /** + * @brief toString + * @return + */ + virtual QString toString(bool) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + + /** + * @brief generateDotTree + */ + virtual void generateDotTree(QString&); + + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + /** + * @brief getConditionType + * @return + */ + Dice::ConditionType getConditionType() const; + + /** + * @brief setConditionType + * @param conditionType + */ + void setConditionType(const Dice::ConditionType& conditionType); + +protected: + ExecutionNode* getLeafNode(ExecutionNode* node); + +protected: + ValidatorList* m_validatorList= nullptr; + Dice::ConditionType m_conditionType; + + ExecutionNode* m_true; + ExecutionNode* m_false; +}; +#endif diff --git a/src/libparser/node/jumpbackwardnode.cpp b/src/libparser/node/jumpbackwardnode.cpp new file mode 100644 index 0000000..9fa0f45 --- /dev/null +++ b/src/libparser/node/jumpbackwardnode.cpp @@ -0,0 +1,168 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 "jumpbackwardnode.h" +#include <QDebug> + +JumpBackwardNode::JumpBackwardNode() +{ + m_previousNode= nullptr; + m_backwardNode= nullptr; + m_diceResult= new DiceResult(); + m_result= m_diceResult; +} + +qint64 JumpBackwardNode::getPriority() const +{ + return 4; +} +QString JumpBackwardNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"JumpBackwardNode\"]").arg(m_id); + } + else + { + return m_id; + } +} +void JumpBackwardNode::generateDotTree(QString& s) +{ + s.append(toString(true)); + s.append(";\n"); + + if(nullptr != m_backwardNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_backwardNode->toString(false)); + s.append("[label=\"backward\"];\n"); + // m_backwardNode->generateDotTree(s); + } + + if(nullptr != m_nextNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_nextNode->toString(false)); + s.append("[label=\"next\"];\n"); + m_nextNode->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr;\n"); + + if(nullptr != m_result) + { + s.append(toString(false)); + s.append(" ->"); + s.append(m_result->toString(false)); + s.append(" [label=\"Result\"];\n"); + m_result->generateDotTree(s); + } + } +} + +void JumpBackwardNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + ExecutionNode* parent= previous; + bool found= false; + // int i = 3; + Result* result= nullptr; + while((nullptr != parent) && (!found)) + { + result= parent->getResult(); + if(nullptr != result) + { + //--i; + if(/*(i==0)&&*/ (result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST))) + { + found= true; + m_backwardNode= parent; + } + else + { + JumpBackwardNode* jpNode= dynamic_cast<JumpBackwardNode*>(parent); + if(nullptr != jpNode) + { + found= true; + m_backwardNode= parent; + } + } + } + if(!found) + { + parent= parent->getPreviousNode(); + } + } + if(nullptr == result) + { + m_errors.insert( + Dice::ERROR_CODE::DIE_RESULT_EXPECTED, + QObject::tr(" The @ operator expects dice result. Please check the documentation to fix your command.")); + } + else + { + DiceResult* diceResult= dynamic_cast<DiceResult*>(result); + if(nullptr != diceResult) + { + for(auto& die : diceResult->getResultList()) + { + Die* tmpdie= new Die(*die); + //*tmpdie= *die; + m_diceResult->insertResult(tmpdie); + die->displayed(); + } + } + + m_result->setPrevious(previous->getResult()); + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + if(nullptr != diceResult) + { + for(int i= 0; i < diceResult->getResultList().size(); ++i) + { + Die* tmp= diceResult->getResultList().at(i); + Die* tmp2= m_diceResult->getResultList().at(i); + if(tmp->isHighlighted()) + { + tmp2->setHighlighted(true); + } + } + } + } +} + +ExecutionNode* JumpBackwardNode::getCopy() const +{ + JumpBackwardNode* node= new JumpBackwardNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/jumpbackwardnode.h b/src/libparser/node/jumpbackwardnode.h new file mode 100644 index 0000000..598a540 --- /dev/null +++ b/src/libparser/node/jumpbackwardnode.h @@ -0,0 +1,68 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 JUMPBACKWARDNODE_H +#define JUMPBACKWARDNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" +/** + * @brief The JumpBackwardNode class is dedicated to change the previous dice of one dice option. + */ +class JumpBackwardNode : public ExecutionNode +{ +public: + /** + * @brief JumpBackwardNode allows to get result from remote node in the execution tree. + */ + JumpBackwardNode(); + /** + * @brief run - performs the actions + * @param previous + */ + virtual void run(ExecutionNode* previous= nullptr); + + /** + * @brief toString + * @return + */ + virtual QString toString(bool) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief generateDotTree + * @param s + */ + virtual void generateDotTree(QString& s); + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + DiceResult* m_diceResult; + ExecutionNode* m_backwardNode; +}; + +#endif // JUMPBACKWARDNODE_H diff --git a/src/libparser/node/keepdiceexecnode.cpp b/src/libparser/node/keepdiceexecnode.cpp new file mode 100644 index 0000000..b197822 --- /dev/null +++ b/src/libparser/node/keepdiceexecnode.cpp @@ -0,0 +1,131 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 <QList> + +#include "diceparser/parsingtoolbox.h" +#include "keepdiceexecnode.h" + +KeepDiceExecNode::KeepDiceExecNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +KeepDiceExecNode::~KeepDiceExecNode() {} +void KeepDiceExecNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous || nullptr == m_numberOfDiceNode) + { + return; + } + m_numberOfDiceNode->run(previous); + auto lastnode= ParsingToolBox::getLeafNode(m_numberOfDiceNode); + if(nullptr == lastnode) + return; + auto result= lastnode->getResult(); + if(nullptr == result) + return; + if(!result->hasResultOfType(Dice::RESULT_TYPE::SCALAR)) + return; + + auto numberOfDice= result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + + DiceResult* previousDiceResult= dynamic_cast<DiceResult*>(previous->getResult()); + m_result->setPrevious(previousDiceResult); + if(nullptr != previousDiceResult) + { + QList<Die*> diceList= previousDiceResult->getResultList(); + + if(numberOfDice < 0) + { + numberOfDice= diceList.size() + numberOfDice; + } + + QList<Die*> diceList3= diceList.mid(0, static_cast<int>(numberOfDice)); + QList<Die*> diceList2; + + for(Die* die : qAsConst(diceList3)) + { + Die* tmpdie= new Die(*die); + diceList2.append(tmpdie); + die->displayed(); + die->setSelected(false); + } + + if(numberOfDice > static_cast<qint64>(diceList.size())) + { + m_errors.insert(Dice::ERROR_CODE::TOO_MANY_DICE, + QObject::tr(" You ask to keep %1 dice but the result only has %2") + .arg(numberOfDice) + .arg(diceList.size())); + } + + for(auto& tmp : diceList.mid(static_cast<int>(numberOfDice), -1)) + { + tmp->setHighlighted(false); + } + + m_diceResult->setResultList(diceList2); + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } +} + +void KeepDiceExecNode::setDiceKeepNumber(ExecutionNode* n) +{ + m_numberOfDiceNode= n; +} +QString KeepDiceExecNode::toString(bool wl) const +{ + if(wl) + { + // auto param= m_numberOfDiceNode->toString(wl); + return QString("%1 [label=\"KeepDiceExecNode\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 KeepDiceExecNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* KeepDiceExecNode::getCopy() const +{ + KeepDiceExecNode* node= new KeepDiceExecNode(); + if(nullptr != m_numberOfDiceNode) + { + node->setDiceKeepNumber(m_numberOfDiceNode->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/keepdiceexecnode.h b/src/libparser/node/keepdiceexecnode.h new file mode 100644 index 0000000..0dd6616 --- /dev/null +++ b/src/libparser/node/keepdiceexecnode.h @@ -0,0 +1,47 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 KEEPDICEEXECNODE_H +#define KEEPDICEEXECNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" +/** + * @brief The KeepDiceExecNode class splits the dice result to get the m_numberOfDice dice from the beginning of the + * dice result. Usually the pervious node of an KeepDiceExecNode is an SortNode. + */ +class KeepDiceExecNode : public ExecutionNode +{ +public: + KeepDiceExecNode(); + virtual ~KeepDiceExecNode(); + + virtual void run(ExecutionNode* previous); + virtual void setDiceKeepNumber(ExecutionNode* valueNode ); + virtual QString toString(bool) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + +private: + ExecutionNode* m_numberOfDiceNode = nullptr; + DiceResult* m_diceResult; +}; + +#endif // KEEPDICEEXECNODE_H diff --git a/src/libparser/node/listaliasnode.cpp b/src/libparser/node/listaliasnode.cpp new file mode 100644 index 0000000..c3f6d31 --- /dev/null +++ b/src/libparser/node/listaliasnode.cpp @@ -0,0 +1,85 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 "listaliasnode.h" + +ListAliasNode::ListAliasNode(const QList<DiceAlias*>& apAlias) : m_aliasList(apAlias) +{ + m_result= new StringResult(); +} +void ListAliasNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + StringResult* txtResult= dynamic_cast<StringResult*>(m_result); + txtResult->setHighLight(false); + + txtResult->addText(buildList()); + txtResult->finished(); + if(nullptr != previous) + { + // txtResult->setText(previous->getHelp()); + m_result->setPrevious(previous->getResult()); + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +QString ListAliasNode::buildList() const +{ + QString result(QObject::tr("List of Alias:\n")); + for(auto& key : m_aliasList) + { + result+= QString("%1 : %2 # %3\n").arg(key->pattern(), key->command(), key->comment()); + } + return result; +} +QString ListAliasNode::toString(bool wl) const +{ + QStringList resultList; + for(auto& key : m_aliasList) + { + resultList << "{" << key->pattern() << key->command() << "}"; + } + + if(wl) + { + return QString("%1 [label=\"ListAliasNode %2\"]").arg(m_id, resultList.join(",")); + } + else + { + return m_id; + } +} +qint64 ListAliasNode::getPriority() const +{ + return 0; +} + +ExecutionNode* ListAliasNode::getCopy() const +{ + ListAliasNode* node= new ListAliasNode(m_aliasList); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/listaliasnode.h b/src/libparser/node/listaliasnode.h new file mode 100644 index 0000000..2a6f96e --- /dev/null +++ b/src/libparser/node/listaliasnode.h @@ -0,0 +1,64 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 LISTALIASNODE_H +#define LISTALIASNODE_H + +#include <diceparser/dicealias.h> + +#include "executionnode.h" +#include "result/stringresult.h" + +/** + * @brief The ListAliasNode class is dedicated to display the list of the current aliases. + */ +class ListAliasNode : public ExecutionNode +{ +public: + ListAliasNode(const QList<DiceAlias*>& mapAlias); + /** + * @brief run + * @param previous + */ + virtual void run(ExecutionNode* previous= nullptr); + + /** + * @brief toString + * @return + */ + virtual QString toString(bool) const; + /** + * @brief buildList + * @return + */ + virtual QString buildList() const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + + virtual ExecutionNode* getCopy() const; + +private: + const QList<DiceAlias*>& m_aliasList; +}; + +#endif // LISTALIASNODE_H diff --git a/src/libparser/node/listsetrollnode.cpp b/src/libparser/node/listsetrollnode.cpp new file mode 100644 index 0000000..2cee645 --- /dev/null +++ b/src/libparser/node/listsetrollnode.cpp @@ -0,0 +1,190 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 "listsetrollnode.h" +#include "die.h" + +#include <QDebug> + +ListSetRollNode::ListSetRollNode() : m_diceResult(new DiceResult()), m_stringResult(new StringResult()), m_unique(false) +{ + m_result= m_stringResult; +} +ListSetRollNode::~ListSetRollNode() +{ + if(nullptr != m_diceResult) + { + delete m_diceResult; + m_diceResult= nullptr; + } +} + +QStringList ListSetRollNode::getList() const +{ + return m_values; +} +QString ListSetRollNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"ListSetRoll list:%2\"]").arg(m_id, m_values.join(",")); + } + else + { + return m_id; + } +} +qint64 ListSetRollNode::getPriority() const +{ + qint64 priority= 4; + return priority; +} +void ListSetRollNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + Result* result= previous->getResult(); + if(nullptr != result) + { + quint64 diceCount= result->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + if(diceCount > static_cast<quint64>(m_values.size()) && m_unique) + { + m_errors.insert(Dice::ERROR_CODE::TOO_MANY_DICE, + QObject::tr("More unique values asked than possible values (L operator)")); + } + else + { + m_result->setPrevious(result); + for(quint64 i= 0; i < diceCount; ++i) + { + QStringList rollResult; + Die* die= new Die(); + computeFacesNumber(die); + die->roll(); + m_diceResult->insertResult(die); + getValueFromDie(die, rollResult); + for(auto const& str : qAsConst(rollResult)) + { + m_stringResult->addText(str); + } + } + m_stringResult->finished(); + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + } +} +void ListSetRollNode::setListValue(QStringList lirs) +{ + m_values= lirs; +} +void ListSetRollNode::setUnique(bool u) +{ + m_unique= u; +} +void ListSetRollNode::setNoComma(bool b) +{ + if(m_stringResult) + m_stringResult->setNoComma(b); +} +void ListSetRollNode::setRangeList(QList<Range>& ranges) +{ + m_rangeList= ranges; +} +void ListSetRollNode::computeFacesNumber(Die* die) +{ + if(m_rangeList.isEmpty()) + { + die->setMaxValue(m_values.size()); + } + else + { + Q_ASSERT(m_values.size() == m_rangeList.size()); + qint64 max; + int i= 0; + for(Range& range : m_rangeList) + { + if(((i == 0) || (max < range.getEnd())) && (range.isFullyDefined())) + { + max= range.getEnd(); + } + ++i; + } + die->setMaxValue(max); + } +} +void ListSetRollNode::getValueFromDie(Die* die, QStringList& rollResult) +{ + if(m_rangeList.isEmpty()) + { + if(die->getValue() - 1 < m_values.size()) + { + auto str= m_values[die->getValue() - 1]; + while(m_unique && rollResult.contains(str)) + { + die->roll(false); + str= m_values[die->getValue() - 1]; + } + rollResult << str; + } + } + else + { + Q_ASSERT(m_values.size() == m_rangeList.size()); + bool found= false; + while(!found) + { + int i= 0; + for(Range& range : m_rangeList) + { + auto it= std::find(m_rangeIndexResult.begin(), m_rangeIndexResult.end(), i); + auto isValid= range.hasValid(die, false); + if((isValid && !m_unique) || (isValid && it == m_rangeIndexResult.end())) + { + m_rangeIndexResult.push_back(i); + rollResult << m_values[i]; + found= true; + } + ++i; + } + if(!found) + { + die->roll(false); + } + } + } +} +ExecutionNode* ListSetRollNode::getCopy() const +{ + ListSetRollNode* node= new ListSetRollNode(); + QList<Range> dataList= m_rangeList; + node->setRangeList(dataList); + node->setUnique(m_unique); + node->setListValue(m_values); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/listsetrollnode.h b/src/libparser/node/listsetrollnode.h new file mode 100644 index 0000000..a6c5e1a --- /dev/null +++ b/src/libparser/node/listsetrollnode.h @@ -0,0 +1,62 @@ +/************************************************************************* + * Copyright (C) 2009 by Renaud Guezennec * + * * + * https://rolisteam.org/ * + * * + * rolisteam 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 LISTSETROLLNODE_H +#define LISTSETROLLNODE_H + +#include <QStringList> + +#include "executionnode.h" +#include "range.h" +#include "result/diceresult.h" +#include "result/stringresult.h" +/** + * @brief The ListSetRollNode class is dedicated to pick up item from list. + */ +class ListSetRollNode : public ExecutionNode +{ +public: + ListSetRollNode(); + virtual ~ListSetRollNode(); + virtual void run(ExecutionNode* previous= nullptr); + virtual QString toString(bool) const; + virtual qint64 getPriority() const; + QStringList getList() const; + + void setListValue(QStringList); + void setUnique(bool); + void setNoComma(bool); + void setRangeList(QList<Range>&); + virtual ExecutionNode* getCopy() const; + +private: + void getValueFromDie(Die* die, QStringList& rollResult); + void computeFacesNumber(Die* die); + +private: + QStringList m_values; + DiceResult* m_diceResult; + StringResult* m_stringResult; + std::vector<int> m_rangeIndexResult; + bool m_unique; + QList<Range> m_rangeList; +}; + +#endif // LISTSETROLLNODE_H diff --git a/src/libparser/node/mergenode.cpp b/src/libparser/node/mergenode.cpp new file mode 100644 index 0000000..096bb8c --- /dev/null +++ b/src/libparser/node/mergenode.cpp @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "mergenode.h" + +#include <diceparser/parsingtoolbox.h> + +MergeNode::MergeNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +void MergeNode::run(ExecutionNode* previous) +{ + if(nullptr == previous) + { + m_errors.insert(Dice::ERROR_CODE::NO_PREVIOUS_ERROR, QObject::tr("No previous node before Merge operator")); + return; + } + + m_previousNode= previous; + m_result->setPrevious(previous->getResult()); + ExecutionNode* previousLast= nullptr; + std::vector<Result*> pastResult; + for(auto start : *m_startList) + { + ExecutionNode* last= getLatestNode(start); + if(nullptr == last || nullptr == previousLast) + { + previousLast= last; + continue; + } + + auto startResult= start->getResult(); + if(nullptr == startResult) + continue; + + startResult->setPrevious(previousLast->getResult()); + previousLast->setNextNode(start); + + previousLast= last; + Result* tmpResult= last->getResult(); + while(nullptr != tmpResult) + { + DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult); + if(nullptr != dice) + { + ///@todo TODO improve here to set homogeneous while is really + m_diceResult->setHomogeneous(false); + for(auto& die : dice->getResultList()) + { + if(!m_diceResult->getResultList().contains(die) && (!die->hasBeenDisplayed())) + { + Die* tmpdie= new Die(*die); + die->displayed(); + m_diceResult->getResultList().append(tmpdie); + } + } + } + auto it= std::find_if(pastResult.begin(), pastResult.end(), + [tmpResult](const Result* a) { return (a == tmpResult->getPrevious()); }); + if(it == pastResult.end()) + { + pastResult.push_back(previousLast->getResult()); + tmpResult= tmpResult->getPrevious(); + } + else + { + tmpResult->setPrevious(nullptr); + tmpResult= nullptr; + } + } + } + + auto first= m_startList->front(); + m_startList->clear(); + m_startList->push_back(first); + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +#include <QDebug> +ExecutionNode* MergeNode::getLatestNode(ExecutionNode* node) +{ + ExecutionNode* next= node; + while(nullptr != next->getNextNode() && (next->getNextNode() != this)) + { + // qDebug() << "find latest node" << next->toString(true) << next->getNextNode()->toString(true); + next= next->getNextNode(); + } + return next; +} +QString MergeNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"Merge Node\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 MergeNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} +ExecutionNode* MergeNode::getCopy() const +{ + MergeNode* node= new MergeNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +std::vector<ExecutionNode*>* MergeNode::getStartList() const +{ + return m_startList; +} + +void MergeNode::setStartList(std::vector<ExecutionNode*>* startList) +{ + m_startList= startList; +} diff --git a/src/libparser/node/mergenode.h b/src/libparser/node/mergenode.h new file mode 100644 index 0000000..515a2e9 --- /dev/null +++ b/src/libparser/node/mergenode.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 MERGENODE_H +#define MERGENODE_H + +#include "node/executionnode.h" +#include "result/diceresult.h" + +/** + * @brief The MergeNode class is an ExecutionNode. It is dedicated to merge result of several commands. + */ +class MergeNode : public ExecutionNode +{ +public: + MergeNode(); + void run(ExecutionNode* previous); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + std::vector<ExecutionNode*>* getStartList() const; + void setStartList(std::vector<ExecutionNode*>* startList); + +private: + ExecutionNode* getLatestNode(ExecutionNode* node); + +private: + DiceResult* m_diceResult= nullptr; + std::vector<ExecutionNode*>* m_startList= nullptr; +}; + +#endif // NUMBERNODE_H diff --git a/src/libparser/node/node.pri b/src/libparser/node/node.pri new file mode 100644 index 0000000..6cf7005 --- /dev/null +++ b/src/libparser/node/node.pri @@ -0,0 +1,33 @@ +HEADERS += \ + $$PWD/dicerollernode.h \ + $$PWD/executionnode.h \ + $$PWD/rerolldicenode.h \ + $$PWD/startingnode.h \ + $$PWD/scalaroperatornode.h \ + $$PWD/numbernode.h \ + $$PWD/sortresult.h \ + $$PWD/keepdiceexecnode.h \ + $$PWD/countexecutenode.h \ + $$PWD/explodedicenode.h \ + $$PWD/parenthesesnode.h \ + $$PWD/helpnode.h \ + $$PWD/jumpbackwardnode.h \ + $$PWD/listsetrollnode.h\ + $$PWD/listaliasnode.h + +SOURCES += \ + $$PWD/dicerollernode.cpp \ + $$PWD/executionnode.cpp \ + $$PWD/startingnode.cpp \ + $$PWD/rerolldicenode.cpp \ + $$PWD/scalaroperatornode.cpp \ + $$PWD/numbernode.cpp \ + $$PWD/sortresult.cpp \ + $$PWD/keepdiceexecnode.cpp \ + $$PWD/countexecutenode.cpp \ + $$PWD/explodedicenode.cpp \ + $$PWD/parenthesesnode.cpp \ + $$PWD/helpnode.cpp \ + $$PWD/jumpbackwardnode.cpp \ + $$PWD/listsetrollnode.cpp\ + $$PWD/listaliasnode.cpp diff --git a/src/libparser/node/numbernode.cpp b/src/libparser/node/numbernode.cpp new file mode 100644 index 0000000..e50656e --- /dev/null +++ b/src/libparser/node/numbernode.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "numbernode.h" + +NumberNode::NumberNode() : m_scalarResult(new ScalarResult()) +{ + m_result= m_scalarResult; +} +NumberNode::~NumberNode() +{ + /*if( nullptr != m_scalarResult) + { + delete m_scalarResult; + m_scalarResult = nullptr; + }*/ +} + +void NumberNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + m_result->setPrevious(previous->getResult()); + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +void NumberNode::setNumber(qint64 a) +{ + m_scalarResult->setValue(a); + m_number= a; +} +QString NumberNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"NumberNode %2\"]").arg(m_id).arg(m_number); + } + else + { + return m_id; + } +} +qint64 NumberNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* NumberNode::getCopy() const +{ + NumberNode* node= new NumberNode(); + node->setNumber(m_number); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/numbernode.h b/src/libparser/node/numbernode.h new file mode 100644 index 0000000..02df984 --- /dev/null +++ b/src/libparser/node/numbernode.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 NUMBERNODE_H +#define NUMBERNODE_H + +#include "node/executionnode.h" +#include "result/scalarresult.h" + +/** + * @brief The NumberNode class is an ExecutionNode. It is dedicated to store number. + */ +class NumberNode : public ExecutionNode +{ +public: + NumberNode(); + virtual ~NumberNode(); + void run(ExecutionNode* previous); + void setNumber(qint64); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + +private: + qint64 m_number; + ScalarResult* m_scalarResult; +}; + +#endif // NUMBERNODE_H diff --git a/src/libparser/node/occurencecountnode.cpp b/src/libparser/node/occurencecountnode.cpp new file mode 100644 index 0000000..fc5c2f2 --- /dev/null +++ b/src/libparser/node/occurencecountnode.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2018 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 "occurencecountnode.h" +#include "result/diceresult.h" +#include "result/stringresult.h" +#include "validatorlist.h" +#include <QVector> + +OccurenceCountNode::OccurenceCountNode() : ExecutionNode() {} + +void OccurenceCountNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + std::map<qint64, qint64> mapOccurence; + if(nullptr == m_previousNode) + return; + + DiceResult* previousDiceResult= dynamic_cast<DiceResult*>(m_previousNode->getResult()); + if(nullptr == previousDiceResult) + return; + + auto const& diceList= previousDiceResult->getResultList(); + QVector<qint64> vec; + + for(auto dice : diceList) + { + auto val= dice->getValue(); + + vec << val; + auto it= mapOccurence.find(val); + if(it == mapOccurence.end()) + mapOccurence[val]= 1; + else + mapOccurence[val]+= 1; + } + + std::sort(vec.begin(), vec.end()); + if(nullptr == m_nextNode) + { + runForStringResult(mapOccurence, vec); + } + else + { + runForDiceResult(mapOccurence); + } +} +QString OccurenceCountNode::toString(bool label) const +{ + if(label) + { + return QString("%1 [label=\"OccurenceCountNode %2\"]").arg(m_id); + } + else + { + return m_id; + } +} +ExecutionNode* OccurenceCountNode::getCopy() const +{ + return nullptr; +} +qint64 OccurenceCountNode::getPriority() const +{ + qint64 priority= 0; + + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +qint64 OccurenceCountNode::getWidth() const +{ + return m_width; +} + +void OccurenceCountNode::setWidth(const qint64& width) +{ + m_width= width; +} + +qint64 OccurenceCountNode::getHeight() const +{ + return m_height; +} + +void OccurenceCountNode::setHeight(const qint64& height) +{ + m_height= height; +} + +ValidatorList* OccurenceCountNode::getValidatorList() const +{ + return m_validatorList; +} + +void OccurenceCountNode::setValidatorList(ValidatorList* validatorlist) +{ + m_validatorList= validatorlist; +} +void OccurenceCountNode::runForStringResult(const std::map<qint64, qint64>& mapOccurence, QVector<qint64>& vec) +{ + m_stringResult= new StringResult(); + m_result= m_stringResult; + QStringList list; + for(auto key : mapOccurence) + { + if(nullptr != m_validatorList) + { + Die die; + die.insertRollValue(key.first); + if(!m_validatorList->hasValid(&die, true)) + continue; + } + + if(key.second < m_width) + continue; + + if(key.first >= m_height) + list << QStringLiteral("%1x%2").arg(key.second).arg(key.first); + } + + QStringList resultList; + std::for_each(vec.begin(), vec.end(), [&resultList](qint64 val) { resultList << QString::number(val); }); + + QString result; + + if(!list.isEmpty()) + result= list.join(','); + else + result= QObject::tr("No matching result"); + + m_stringResult->addText(QStringLiteral("%1 - [%2]").arg(result).arg(resultList.join(','))); + m_stringResult->finished(); +} +void OccurenceCountNode::runForDiceResult(const std::map<qint64, qint64>& mapOccurence) +{ + m_diceResult= new DiceResult(); + m_result= m_diceResult; + QStringList list; + for(auto key : mapOccurence) + { + if(nullptr != m_validatorList) + { + Die die; + die.insertRollValue(key.first); + if(!m_validatorList->hasValid(&die, true)) + continue; + } + + if(key.second < m_width) + continue; + + if(key.first >= m_height) + { + // list << QStringLiteral("%1x%2").arg(key.second).arg(key.first); + Die* die= new Die(); + die->insertRollValue(key.second * key.first); + m_diceResult->insertResult(die); + } + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} diff --git a/src/libparser/node/occurencecountnode.h b/src/libparser/node/occurencecountnode.h new file mode 100644 index 0000000..4801bfb --- /dev/null +++ b/src/libparser/node/occurencecountnode.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2018 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 OCCURENCECOUNTNODE_H +#define OCCURENCECOUNTNODE_H + +#include "executionnode.h" + +class ValidatorList; +class StringResult; +class DiceResult; +class OccurenceCountNode : public ExecutionNode +{ +public: + OccurenceCountNode(); + + void run(ExecutionNode* previous= nullptr); + virtual QString toString(bool withLabel) const; + + ExecutionNode* getCopy() const; + qint64 getPriority() const; + + qint64 getWidth() const; + void setWidth(const qint64& width); + + qint64 getHeight() const; + void setHeight(const qint64& height); + + ValidatorList* getValidatorList() const; + void setValidatorList(ValidatorList* validator); + +private: + void runForStringResult(const std::map<qint64, qint64>& mapOccurence, QVector<qint64>& vec); + void runForDiceResult(const std::map<qint64, qint64>& mapOccurence); + +private: + qint64 m_width= 1; + qint64 m_height= 0; + ValidatorList* m_validatorList= nullptr; + StringResult* m_stringResult= nullptr; + DiceResult* m_diceResult= nullptr; +}; + +#endif // OCCURENCECOUNTNODE_H diff --git a/src/libparser/node/paintnode.cpp b/src/libparser/node/paintnode.cpp new file mode 100644 index 0000000..22c020e --- /dev/null +++ b/src/libparser/node/paintnode.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 "paintnode.h" + +ColorItem::ColorItem(QString str, int val) : m_colorNumber(val), m_color(str) {} + +int ColorItem::colorNumber() const +{ + return m_colorNumber; +} + +void ColorItem::setColorNumber(int colorNumber) +{ + m_colorNumber= colorNumber; +} + +QString ColorItem::color() const +{ + return m_color; +} + +void ColorItem::setColor(const QString& color) +{ + m_color= color; +} + +/////////////////////////////////// +/// @brief PainterNode::PainterNode +/////////////////////////////////// + +PainterNode::PainterNode() : ExecutionNode() +{ + m_nextNode= nullptr; +} + +PainterNode::~PainterNode() +{ + m_result= nullptr; +} + +void PainterNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + m_errors.insert(Dice::ERROR_CODE::NO_PREVIOUS_ERROR, QObject::tr("No previous node before Paint operator")); + return; + } + Result* previousResult= previous->getResult(); + if(nullptr == previousResult) + return; + + m_diceResult= dynamic_cast<DiceResult*>(previousResult->getCopy()); + if(nullptr != m_diceResult) + { + QList<Die*> diceList= m_diceResult->getResultList(); + int pastDice= 0; + for(ColorItem& item : m_colors) + { + int current= item.colorNumber(); + QList<Die*>::iterator it; + for(it= diceList.begin() + pastDice; it != diceList.end() && current > 0; ++it) + { + (*it)->setColor(item.color()); + --current; + ++pastDice; + } + } + m_diceResult->setPrevious(previousResult); + m_result= m_diceResult; + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString PainterNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"PainterNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 PainterNode::getPriority() const +{ + return 4; +} + +void PainterNode::insertColorItem(QString color, int value) +{ + ColorItem item(color, value); + m_colors.append(item); +} +ExecutionNode* PainterNode::getCopy() const +{ + PainterNode* node= new PainterNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/paintnode.h b/src/libparser/node/paintnode.h new file mode 100644 index 0000000..06d849e --- /dev/null +++ b/src/libparser/node/paintnode.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * http:://www.rolisteam.org/contact * + * * + * rolisteam 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 PAINTERNODE_H +#define PAINTERNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" +#include <QString> + +class ColorItem +{ +public: + ColorItem(QString str, int val); + int colorNumber() const; + void setColorNumber(int colorNumber); + + QString color() const; + void setColor(const QString& color); + +private: + int m_colorNumber; + QString m_color; +}; +/** + * @brief The PainterNode class means to manage color attribute of dice. + */ +class PainterNode : public ExecutionNode +{ +public: + PainterNode(); + virtual ~PainterNode(); + virtual void run(ExecutionNode* previous= nullptr); + virtual QString toString(bool) const; + virtual qint64 getPriority() const; + void insertColorItem(QString color, int value); + virtual ExecutionNode* getCopy() const; + +protected: + QList<ColorItem> m_colors; + DiceResult* m_diceResult= nullptr; +}; + +#endif diff --git a/src/libparser/node/parenthesesnode.cpp b/src/libparser/node/parenthesesnode.cpp new file mode 100644 index 0000000..9557536 --- /dev/null +++ b/src/libparser/node/parenthesesnode.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "parenthesesnode.h" + +ParenthesesNode::ParenthesesNode() : m_internalNode(nullptr) {} +void ParenthesesNode::setInternelNode(ExecutionNode* node) +{ + m_internalNode= node; +} +void ParenthesesNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != m_internalNode) + { + m_internalNode->run(this); + ExecutionNode* temp= m_internalNode; + while(nullptr != temp->getNextNode()) + { + temp= temp->getNextNode(); + } + m_result= temp->getResult(); + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +QString ParenthesesNode::toString(bool b) const +{ + if(b) + { + return QString("%1 [label=\"ParenthesesNode\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 ParenthesesNode::getPriority() const +{ + qint64 priority= 3; + return priority; +} +ExecutionNode* ParenthesesNode::getCopy() const +{ + ParenthesesNode* node= new ParenthesesNode(); + if(nullptr != m_internalNode) + { + node->setInternelNode(m_internalNode->getCopy()); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +void ParenthesesNode::generateDotTree(QString& s) +{ + auto str= toString(true); + if(s.contains(str)) + return; + s.append(str); + s.append(";\n"); + + if(nullptr != m_internalNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_internalNode->toString(false)); + s.append("[label=\"internal\"];\n"); + m_internalNode->generateDotTree(s); + } + + if(nullptr != m_nextNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_nextNode->toString(false)); + s.append(" [label=\"next\"];\n"); + // s.append(" [label=\"nextNode\"];\n"); + m_nextNode->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr;\n"); + } + if(nullptr != m_result) + { + s.append(toString(false)); + s.append(" ->"); + s.append(m_result->toString(false)); + s.append(" [label=\"Result\", style=\"dashed\"];\n"); + if(nullptr == m_nextNode) + m_result->generateDotTree(s); + } +} diff --git a/src/libparser/node/parenthesesnode.h b/src/libparser/node/parenthesesnode.h new file mode 100644 index 0000000..153dfc1 --- /dev/null +++ b/src/libparser/node/parenthesesnode.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 PARENTHESESNODE_H +#define PARENTHESESNODE_H + +#include "executionnode.h" +/** + * @brief The ParenthesesNode class is an ExecutionNode. It is dedicated to manage expression which was inside + * ParenthesesNode. It is acting just like an StartingNode by for an internal execution tree. + */ +class ParenthesesNode : public ExecutionNode +{ +public: + ParenthesesNode(); + virtual void run(ExecutionNode* previous= nullptr); + + void setInternelNode(ExecutionNode* node); + virtual QString toString(bool) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + virtual void generateDotTree(QString&); + +private: + ExecutionNode* m_internalNode; +}; + +#endif // PARENTHESESNODE_H diff --git a/src/libparser/node/repeaternode.cpp b/src/libparser/node/repeaternode.cpp new file mode 100644 index 0000000..f93a9fe --- /dev/null +++ b/src/libparser/node/repeaternode.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + * Copyright (C) 2019 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "node/repeaternode.h" + +#include "executionnode.h" +#include "result/scalarresult.h" +#include "result/stringresult.h" +#include <QDebug> +#include <diceparser/diceparserhelper.h> +#include <diceparser/parsingtoolbox.h> + +using InstructionSet= std::vector<ExecutionNode*>; + +QStringList allFirstResultAsString(const InstructionSet& startingNodes, bool& hasAlias) +{ + ParsingToolBox parsingBox; + // QStringList allResult; + QStringList stringListResult; + for(auto node : startingNodes) + { + auto pair= parsingBox.hasResultOfType(Dice::RESULT_TYPE::STRING, node); + auto pairStr= parsingBox.hasResultOfType(Dice::RESULT_TYPE::SCALAR, node, true); + if(pair.first) + { + stringListResult << pair.second.toString(); + hasAlias= true; + } + else if(pairStr.first) + { + stringListResult << QString::number(pairStr.second.toReal()); + hasAlias= true; + } + } + return stringListResult; +} + +std::vector<ExecutionNode*> makeCopy(std::vector<ExecutionNode*> cmds) +{ + std::vector<ExecutionNode*> copy; + std::transform(cmds.begin(), cmds.end(), std::back_inserter(copy), + [](ExecutionNode* node) { return node->getCopy(); }); + return copy; +} + +RepeaterNode::RepeaterNode() {} + +void RepeaterNode::run(ExecutionNode* previousNode) +{ + m_previousNode= previousNode; + + if(nullptr == m_times || m_cmd.empty()) + return; + + m_times->run(this); + m_times= ParsingToolBox::getLeafNode(m_times); + auto times= m_times->getResult(); + if(!times) + return; + + std::vector<InstructionSet> m_startingNodes; + auto timeCount= times->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + auto cmd= makeCopy(m_cmd); + std::vector<Result*> resultVec; + for(int i= 0; i < timeCount; ++i) + { + m_startingNodes.push_back(cmd); + std::for_each(cmd.begin(), cmd.end(), + [this, &resultVec](ExecutionNode* node) + { + node->run(this); + auto end= ParsingToolBox::getLeafNode(node); + auto leafResult= end->getResult(); + + if(nullptr == leafResult) + return; + + resultVec.push_back(leafResult); + }); + cmd= makeCopy(m_cmd); + } + if(m_sumAll) + { + auto scalar= new ScalarResult(); + qreal value= 0.0; + std::for_each(resultVec.begin(), resultVec.end(), + [&value](Result* result) { value+= result->getResult(Dice::RESULT_TYPE::SCALAR).toDouble(); }); + scalar->setValue(value); + m_result= scalar; + } + else + { + auto string= new StringResult(); + + QStringList listOfStrResult; + for(auto instructions : m_startingNodes) + { + ParsingToolBox parsingBox; + parsingBox.setStartNodes(instructions); + auto finalString + = parsingBox.finalStringResult([](const QString& result, const QString&, bool) { return result; }); + listOfStrResult << finalString; + } + if(!listOfStrResult.isEmpty()) + string->addText(listOfStrResult.join('\n')); + + m_result= string; + + // qDebug().noquote() << listOfStrResult.join('\n'); + } + + if(nullptr != m_nextNode) + m_nextNode->run(this); +} + +QString RepeaterNode::toString(bool withLabel) const +{ + return withLabel ? QStringLiteral("") : QStringLiteral(""); +} + +qint64 RepeaterNode::getPriority() const +{ + return 4; +} + +ExecutionNode* RepeaterNode::getCopy() const +{ + return nullptr; +} + +void RepeaterNode::setCommand(const std::vector<ExecutionNode*>& cmd) +{ + m_cmd= cmd; +} + +void RepeaterNode::setTimeNode(ExecutionNode* time) +{ + m_times= time; +} + +void RepeaterNode::setSumAll(bool b) +{ + m_sumAll= b; +} diff --git a/src/libparser/node/repeaternode.h b/src/libparser/node/repeaternode.h new file mode 100644 index 0000000..fa1a50a --- /dev/null +++ b/src/libparser/node/repeaternode.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2019 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 REPEATER_NODE_H +#define REPEATER_NODE_H + +#include "node/executionnode.h" +#include <memory> + +class RepeaterNode : public ExecutionNode +{ +public: + RepeaterNode(); + void run(ExecutionNode* previous) override; + virtual QString toString(bool withLabel) const override; + virtual qint64 getPriority() const override; + + virtual ExecutionNode* getCopy() const override; + + void setCommand(const std::vector<ExecutionNode*>& node); + void setTimeNode(ExecutionNode* times); + void setSumAll(bool b); + +private: + std::vector<ExecutionNode*> m_cmd; + ExecutionNode* m_times= nullptr; + bool m_sumAll= false; +}; + +#endif // REPEATER_NODE_H diff --git a/src/libparser/node/replacevaluenode.cpp b/src/libparser/node/replacevaluenode.cpp new file mode 100644 index 0000000..c313fb0 --- /dev/null +++ b/src/libparser/node/replacevaluenode.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * 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 "replacevaluenode.h" + +#include "diceresult.h" +#include <QDebug> +#include <diceparser/parsingtoolbox.h> + +ReplaceValueNode::ReplaceValueNode() : m_diceResult(new DiceResult) +{ + m_result= m_diceResult; +} + +void ReplaceValueNode::setStopAtFirt(bool b) +{ + m_stopAtFirst= b; +} + +void ReplaceValueNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + m_errors.insert(Dice::ERROR_CODE::NO_PREVIOUS_ERROR, + QStringLiteral("No previous node before Switch/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 Switch/Case operator")); + return; + } + + QList<Die*> dieList; + if(previousResult->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + auto diceResult= dynamic_cast<DiceResult*>(previousResult); + if(diceResult) + dieList.append(diceResult->getResultList()); + } + + for(auto die : dieList) + { + QStringList resultList; + for(auto const& info : qAsConst(m_branchList)) + { + if(info->validatorList) + { + auto res= info->validatorList->hasValid(die, false); + if(!res) + continue; + } + else if(!resultList.isEmpty()) + break; + + auto replaceValresult= info->node->getResult(); + if(replaceValresult) + die->replaceLastValue(replaceValresult->getResult(Dice::RESULT_TYPE::SCALAR).toInt()); + break; + } + m_diceResult->insertResult(die); + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString ReplaceValueNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"ReplaceValueNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 ReplaceValueNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* ReplaceValueNode::getCopy() const +{ + ReplaceValueNode* node= new ReplaceValueNode(); + 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 ReplaceValueNode::insertCase(ExecutionNode* node, ValidatorList* validator) +{ + std::unique_ptr<Dice::CaseInfo> info(new Dice::CaseInfo{validator, node}); + m_branchList.push_back(std::move(info)); +} diff --git a/src/libparser/node/replacevaluenode.h b/src/libparser/node/replacevaluenode.h new file mode 100644 index 0000000..36bdec2 --- /dev/null +++ b/src/libparser/node/replacevaluenode.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * 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 REPLACEVALUENODE_H +#define REPLACEVALUENODE_H + +#include <memory> +#include <vector> + +#include "executionnode.h" +#include "validatorlist.h" + +class DiceResult; +class ReplaceValueNode : public ExecutionNode +{ +public: + ReplaceValueNode(); + 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<Dice::CaseInfo>> m_branchList; + DiceResult* m_diceResult= nullptr; + bool m_stopAtFirst= false; +}; + +#endif // REPLACEVALUENODE_H diff --git a/src/libparser/node/rerolldicenode.cpp b/src/libparser/node/rerolldicenode.cpp new file mode 100644 index 0000000..fd8c258 --- /dev/null +++ b/src/libparser/node/rerolldicenode.cpp @@ -0,0 +1,148 @@ +#include <diceparser/parsingtoolbox.h> + +#include "rerolldicenode.h" +#include "validatorlist.h" +#include <utility> + +RerollDiceNode::RerollDiceNode(bool reroll, bool addingMode) + : m_diceResult(new DiceResult()), m_validatorList(nullptr), m_reroll(reroll), m_adding(addingMode) +{ + m_result= m_diceResult; +} +RerollDiceNode::~RerollDiceNode() +{ + if(nullptr != m_validatorList) + { + delete m_validatorList; + m_validatorList= nullptr; + } +} +void RerollDiceNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if((nullptr != previous) && (nullptr != previous->getResult())) + { + DiceResult* previous_result= dynamic_cast<DiceResult*>(previous->getResult()); + m_result->setPrevious(previous_result); + if(nullptr != previous_result) + { + for(auto& die : previous_result->getResultList()) + { + Die* tmpdie= new Die(*die); + m_diceResult->insertResult(tmpdie); + die->displayed(); + } + // m_diceResult->setResultList(list); + + QList<Die*>& list= m_diceResult->getResultList(); + QList<Die*> toRemove; + + for(auto& die : list) + { + bool finished= false; + auto state= m_validatorList->isValidRangeSize( + std::make_pair<qint64, qint64>(die->getBase(), die->getMaxValue())); + if((Dice::CONDITION_STATE::ALWAYSTRUE == state && m_adding) + || (!m_reroll && !m_adding && state == Dice::CONDITION_STATE::UNREACHABLE)) + { + m_errors.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Condition (%1) cause an endless loop with this dice: %2") + .arg(toString(true)) + .arg(QStringLiteral("d[%1,%2]") + .arg(static_cast<int>(die->getBase())) + .arg(static_cast<int>(die->getMaxValue())))); + continue; + } + while(m_validatorList->hasValid(die, false) && !finished) + { + if(m_instruction != nullptr) + { + m_instruction->run(this); + auto lastNode= ParsingToolBox::getLeafNode(m_instruction); + if(lastNode != nullptr) + { + auto lastResult= dynamic_cast<DiceResult*>(lastNode->getResult()); + if(lastResult != nullptr) + { + toRemove.append(die); + list.append(lastResult->getResultList()); + lastResult->clear(); + } + } + } + else + { + die->roll(m_adding); + } + if(m_reroll) + { + finished= true; + } + } + } + + for(auto die : toRemove) + { + list.removeOne(die); + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + else + { + m_errors.insert( + Dice::ERROR_CODE::DIE_RESULT_EXPECTED, + QObject::tr( + " The a operator expects dice result. Please check the documentation and fix your command.")); + } + } +} +void RerollDiceNode::setValidatorList(ValidatorList* val) +{ + m_validatorList= val; +} +QString RerollDiceNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"RerollDiceNode validatior: %2\"]").arg(m_id, m_validatorList->toString()); + } + else + { + return m_id; + } + // return QString("RerollDiceNode [label=\"RerollDiceNode validatior:%1\""); +} +qint64 RerollDiceNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + + return priority; +} +ExecutionNode* RerollDiceNode::getCopy() const +{ + RerollDiceNode* node= new RerollDiceNode(m_reroll, m_adding); + node->setValidatorList(m_validatorList); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +ExecutionNode* RerollDiceNode::getInstruction() const +{ + return m_instruction; +} + +void RerollDiceNode::setInstruction(ExecutionNode* instruction) +{ + m_instruction= instruction; +} diff --git a/src/libparser/node/rerolldicenode.h b/src/libparser/node/rerolldicenode.h new file mode 100644 index 0000000..68b732e --- /dev/null +++ b/src/libparser/node/rerolldicenode.h @@ -0,0 +1,72 @@ +#ifndef REROLLDICENODE_H +#define REROLLDICENODE_H + +#include "executionnode.h" +#include "result/diceresult.h" + +class ValidatorList; +/** + * @brief The RerollDiceNode class reroll dice given a condition and replace(or add) the result. + */ +class RerollDiceNode : public ExecutionNode +{ +public: + /** + * @brief The ReRollMode enum + */ + enum ReRollMode + { + EQUAL, + LESSER, + GREATER + }; + /** + * @brief RerollDiceNode + * @param reroll If true reroll the dice only once, otherwise until the condition is false + */ + RerollDiceNode(bool reroll, bool addingMode); + + /** + * @brief ~RerollDiceNode + */ + virtual ~RerollDiceNode(); + /** + * @brief run + * @param previous + */ + virtual void run(ExecutionNode* previous); + + /** + * @brief setValidator + */ + virtual void setValidatorList(ValidatorList*); + /** + * @brief toString + * @return + */ + virtual QString toString(bool) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + + ExecutionNode* getInstruction() const; + void setInstruction(ExecutionNode* instruction); + +private: + DiceResult* m_diceResult= nullptr; + ValidatorList* m_validatorList= nullptr; + ExecutionNode* m_instruction= nullptr; + + const bool m_reroll; + const bool m_adding; +}; + +#endif // REROLLDICENODE_H diff --git a/src/libparser/node/scalaroperatornode.cpp b/src/libparser/node/scalaroperatornode.cpp new file mode 100644 index 0000000..c1c4dc5 --- /dev/null +++ b/src/libparser/node/scalaroperatornode.cpp @@ -0,0 +1,286 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "scalaroperatornode.h" + +#include "result/diceresult.h" +#include <QDebug> + +ScalarOperatorNode::ScalarOperatorNode() + : m_internalNode(nullptr), m_scalarResult(new ScalarResult()), m_arithmeticOperator(Dice::ArithmeticOperator::PLUS) +{ + m_result= m_scalarResult; +} +ScalarOperatorNode::~ScalarOperatorNode() +{ + if(nullptr != m_internalNode) + { + delete m_internalNode; + m_internalNode= nullptr; + } +} + +void ScalarOperatorNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != m_internalNode) + { + m_internalNode->run(this); + } + if(nullptr != previous) + { + auto previousResult= previous->getResult(); + + if(nullptr != previousResult) + { + ExecutionNode* internal= m_internalNode; + if(nullptr != internal) + { + while(nullptr != internal->getNextNode()) + { + internal= internal->getNextNode(); + } + + Result* internalResult= internal->getResult(); + m_result->setPrevious(internalResult); + if(nullptr != m_internalNode->getResult()) + { + m_internalNode->getResult()->setPrevious(previousResult); + } + + if(internalResult == nullptr) + { + m_errors.insert(Dice::ERROR_CODE::NO_VALID_RESULT, + QObject::tr("No Valid result in arithmetic operation: %1").arg(toString(true))); + return; + } + + switch(m_arithmeticOperator) + { + case Dice::ArithmeticOperator::PLUS: + m_scalarResult->setValue(add(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal())); + break; + case Dice::ArithmeticOperator::MINUS: + m_scalarResult->setValue(substract(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal())); + break; + case Dice::ArithmeticOperator::MULTIPLICATION: + m_scalarResult->setValue(multiple(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal())); + break; + case Dice::ArithmeticOperator::DIVIDE: + m_scalarResult->setValue(divide(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal())); + break; + case Dice::ArithmeticOperator::INTEGER_DIVIDE: + m_scalarResult->setValue( + static_cast<int>(divide(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal()))); + break; + case Dice::ArithmeticOperator::POW: + m_scalarResult->setValue(pow(previousResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal(), + internalResult->getResult(Dice::RESULT_TYPE::SCALAR).toReal())); + break; + } + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + } +} +/*bool ScalarOperatorNode::setOperatorChar(QChar c) +{ + if(m_scalarOperationList.contains(c)) + { + m_operator = m_scalarOperationList.value(c); + return true; + } + return false; +}*/ + +void ScalarOperatorNode::setInternalNode(ExecutionNode* node) +{ + m_internalNode= node; +} +qint64 ScalarOperatorNode::add(qreal a, qreal b) +{ + return static_cast<qint64>(a + b); +} +qint64 ScalarOperatorNode::substract(qreal a, qreal b) +{ + return static_cast<qint64>(a - b); +} +qreal ScalarOperatorNode::divide(qreal a, qreal b) +{ + if(qFuzzyCompare(b, 0)) + { + m_errors.insert(Dice::ERROR_CODE::DIVIDE_BY_ZERO, QObject::tr("Division by zero")); + return 0; + } + return static_cast<qreal>(a / b); +} +qint64 ScalarOperatorNode::multiple(qreal a, qreal b) +{ + return static_cast<qint64>(a * b); +} +qint64 ScalarOperatorNode::pow(qreal a, qreal b) +{ + return static_cast<qint64>(std::pow(a, b)); +} +Dice::ArithmeticOperator ScalarOperatorNode::getArithmeticOperator() const +{ + return m_arithmeticOperator; +} + +void ScalarOperatorNode::setArithmeticOperator(const Dice::ArithmeticOperator& arithmeticOperator) +{ + m_arithmeticOperator= arithmeticOperator; +} + +QString ScalarOperatorNode::toString(bool wl) const +{ + QString op= ""; + switch(m_arithmeticOperator) + { + case Dice::ArithmeticOperator::PLUS: + op= "+"; + break; + case Dice::ArithmeticOperator::MINUS: + op= "-"; + break; + case Dice::ArithmeticOperator::MULTIPLICATION: + op= "*"; + break; + case Dice::ArithmeticOperator::DIVIDE: + op= "/"; + break; + case Dice::ArithmeticOperator::INTEGER_DIVIDE: + op= "|"; + break; + case Dice::ArithmeticOperator::POW: + op= "^"; + break; + } + if(wl) + { + return QString("%1 [label=\"ScalarOperatorNode %2\"]").arg(m_id, op); + } + else + { + return m_id; + } +} +qint64 ScalarOperatorNode::getPriority() const +{ + if((m_arithmeticOperator == Dice::ArithmeticOperator::PLUS) + || (m_arithmeticOperator == Dice::ArithmeticOperator::MINUS)) + { + return 1; + } + else if(m_arithmeticOperator == Dice::ArithmeticOperator::POW) + { + return 3; + } + else + { + return 2; + } +} +void ScalarOperatorNode::generateDotTree(QString& s) +{ + auto id= toString(true); + if(s.contains(id)) + return; + s.append(id); + s.append(";\n"); + + if(nullptr != m_nextNode) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_nextNode->toString(false)); + s.append("[label=\"nextNode\"];\n"); + m_nextNode->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr"); + s.append(" [label=\"nextNode\"];\n"); + } + + if(nullptr != m_result) + { + s.append(toString(false)); + s.append(" ->"); + s.append(m_result->toString(false)); + s.append(" [label=\"Result\", style=\"dashed\"];\n"); + if(nullptr == m_nextNode) + m_result->generateDotTree(s); + } + QString str; + str.append("\n"); + if(nullptr != m_internalNode) + { + str.append(toString(false)); + str.append(" -> "); + str.append(m_internalNode->toString(false)); + str.append(" [label=\"internalNode\"];\n"); + m_internalNode->generateDotTree(str); + } + s.append(str); +} +QMap<Dice::ERROR_CODE, QString> ScalarOperatorNode::getExecutionErrorMap() +{ + if(nullptr != m_internalNode) + { + auto keys= m_internalNode->getExecutionErrorMap().keys(); + for(const auto& key : keys) + { + m_errors.insert(key, m_internalNode->getExecutionErrorMap().value(key)); + } + } + if(nullptr != m_nextNode) + { + auto keys= m_nextNode->getExecutionErrorMap().keys(); + for(auto const& key : keys) + { + m_errors.insert(key, m_nextNode->getExecutionErrorMap().value(key)); + } + } + return m_errors; +} +ExecutionNode* ScalarOperatorNode::getCopy() const +{ + ScalarOperatorNode* node= new ScalarOperatorNode(); + node->setInternalNode(m_internalNode->getCopy()); + node->setArithmeticOperator(m_arithmeticOperator); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/scalaroperatornode.h b/src/libparser/node/scalaroperatornode.h new file mode 100644 index 0000000..57a1049 --- /dev/null +++ b/src/libparser/node/scalaroperatornode.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 SCALAROPERATORNODE_H +#define SCALAROPERATORNODE_H + +#include <QChar> +#include <QMap> + +#include "die.h" +#include "executionnode.h" +#include "result/scalarresult.h" + +/** + * @brief The ScalarOperatorNode class + */ +class ScalarOperatorNode : public ExecutionNode +{ +public: + /** + * @brief The ArithmeticOperator enum + */ + // enum ArithmeticOperator {PLUS,MINUS,DIVIDE,MULTIPLICATION}; + /** + * @brief ScalarOperatorNode + */ + ScalarOperatorNode(); + /** + * @brief ~ScalarOperatorNode + */ + virtual ~ScalarOperatorNode(); + /** + * @brief run + */ + virtual void run(ExecutionNode*); + /** + * @brief setInternalNode + * @param node + */ + void setInternalNode(ExecutionNode* node); + /** + * @brief toString + * @param wl + * @return + */ + virtual QString toString(bool wl) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief generateDotTree + * @param s + */ + void generateDotTree(QString& s); + /** + * @brief getErrorList + * @return + */ + virtual QMap<Dice::ERROR_CODE, QString> getExecutionErrorMap(); + /** + * @brief getArithmeticOperator + * @return + */ + Dice::ArithmeticOperator getArithmeticOperator() const; + /** + * @brief setArithmeticOperator + * @param arithmeticOperator + */ + void setArithmeticOperator(const Dice::ArithmeticOperator& arithmeticOperator); + + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + /** + * @brief add + * @return + */ + static qint64 add(qreal, qreal); + /** + * @brief substract + * @return + */ + static qint64 substract(qreal, qreal); + /** + * @brief divide not static because of error management + * @return + */ + qreal divide(qreal, qreal); + /** + * @brief multiple + * @return + */ + static qint64 multiple(qreal, qreal); + + static qint64 pow(qreal a, qreal b); + +private: + ExecutionNode* m_internalNode; + ScalarResult* m_scalarResult; + Dice::ArithmeticOperator m_arithmeticOperator; +}; + +#endif // SCALAROPERATORNODE_H diff --git a/src/libparser/node/sortresult.cpp b/src/libparser/node/sortresult.cpp new file mode 100644 index 0000000..5d514cf --- /dev/null +++ b/src/libparser/node/sortresult.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "sortresult.h" + +#include "die.h" + +SortResultNode::SortResultNode() : m_diceResult(new DiceResult) +{ + m_ascending= true; + m_result= m_diceResult; +} +void SortResultNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr == previous) + { + return; + } + DiceResult* previousDiceResult= dynamic_cast<DiceResult*>(previous->getResult()); + m_diceResult->setPrevious(previousDiceResult); + if(nullptr == previousDiceResult) + return; + + auto const& diceList= previousDiceResult->getResultList(); + QList<Die*> diceList2= m_diceResult->getResultList(); + + /* const auto& asce = [](const Die* a,const Die* b){ + return a->getValue() < b->getValue(); + }; + const auto& desc = [](const Die* a,const Die* b){ + return a->getValue() > b->getValue(); + }; + + for(auto const dice : diceList) + { + Die* tmp1 = new Die(*dice); + diceList2.append(tmp1); + } + if(m_ascending) + std::sort(diceList2.begin(), diceList2.end(), asce); + else + std::sort(diceList2.begin(), diceList2.end(), desc);*/ + + // half-interval search sorting + for(int i= 0; i < diceList.size(); ++i) + { + Die* tmp1= new Die(*diceList[i]); + //qDebug() << tmp1->getColor() << diceList[i]->getColor(); + //*tmp1=*diceList[i]; + diceList[i]->displayed(); + + int j= 0; + bool found= false; + int start= 0; + int end= diceList2.size(); + Die* tmp2= nullptr; + while(!found) + { + int distance= end - start; + j= (start + end) / 2; + if(distance == 0) + { + j= end; + found= true; + } + else + { + tmp2= diceList2[j]; + if(tmp1->getValue() < tmp2->getValue()) + { + end= j; + } + else + { + start= j + 1; + } + } + } + diceList2.insert(j, tmp1); + } + + if(!m_ascending) + { + for(int i= 0; i < diceList2.size() / 2; ++i) + { + diceList2.swapItemsAt(i, diceList2.size() - (1 + i)); + } + } + m_diceResult->setResultList(diceList2); + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +void SortResultNode::setSortAscending(bool asc) +{ + m_ascending= asc; +} +QString SortResultNode::toString(bool wl) const +{ + if(wl) + { + auto order= m_ascending ? QStringLiteral("Ascending") : QStringLiteral("Descending"); + return QString("%1 [label=\"SortResultNode %2\"]").arg(m_id, order); + } + else + { + return m_id; + } +} +qint64 SortResultNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} +ExecutionNode* SortResultNode::getCopy() const +{ + SortResultNode* node= new SortResultNode(); + node->setSortAscending(m_ascending); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/sortresult.h b/src/libparser/node/sortresult.h new file mode 100644 index 0000000..f7510be --- /dev/null +++ b/src/libparser/node/sortresult.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 SORTRESULT_H +#define SORTRESULT_H + +#include "executionnode.h" +#include "result/diceresult.h" +/** + * @brief The SortResultNode class is an ExecutionNode, and it is dedicated to sort dice list. + * The sort is made by hand, using half-interval search algorithm. + */ +class SortResultNode : public ExecutionNode +{ +public: + /** + * @brief SortResultNode + */ + SortResultNode(); + /** + * @brief run + */ + virtual void run(ExecutionNode*); + + /** + * @brief setSortAscending + * @param asc + */ + void setSortAscending(bool asc); + /** + * @brief toString + * @return + */ + virtual QString toString(bool wl) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + bool m_ascending; + DiceResult* m_diceResult; +}; + +#endif // SORTRESULT_H diff --git a/src/libparser/node/splitnode.cpp b/src/libparser/node/splitnode.cpp new file mode 100644 index 0000000..8faa0a5 --- /dev/null +++ b/src/libparser/node/splitnode.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "splitnode.h" + +SplitNode::SplitNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +void SplitNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + m_result->setPrevious(previous->getResult()); + + Result* tmpResult= previous->getResult(); + if(nullptr != tmpResult) + { + DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult); + if(nullptr != dice) + { + for(auto& oldDie : dice->getResultList()) + { + oldDie->displayed(); + m_diceResult->setOperator(oldDie->getOp()); + for(qint64& value : oldDie->getListValue()) + { + Die* tmpdie= new Die(); + tmpdie->insertRollValue(value); + tmpdie->setBase(oldDie->getBase()); + tmpdie->setMaxValue(oldDie->getMaxValue()); + tmpdie->setValue(value); + tmpdie->setOp(oldDie->getOp()); + m_diceResult->insertResult(tmpdie); + } + } + } + } + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString SplitNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"SplitNode Node\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 SplitNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* SplitNode::getCopy() const +{ + SplitNode* node= new SplitNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/splitnode.h b/src/libparser/node/splitnode.h new file mode 100644 index 0000000..0ceeb00 --- /dev/null +++ b/src/libparser/node/splitnode.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 SPLITNODE_H +#define SPLITNODE_H + +#include "node/executionnode.h" +#include "result/diceresult.h" + +/** + * @brief The SplitNode class is an ExecutionNode. It is dedicated to split result of one dice into one dimension array. + */ +class SplitNode : public ExecutionNode +{ +public: + SplitNode(); + void run(ExecutionNode* previous); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + +private: + DiceResult* m_diceResult; +}; + +#endif // NUMBERNODE_H diff --git a/src/libparser/node/startingnode.cpp b/src/libparser/node/startingnode.cpp new file mode 100644 index 0000000..97248c6 --- /dev/null +++ b/src/libparser/node/startingnode.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 "startingnode.h" +#include <QDebug> + +StartingNode::StartingNode() {} +void StartingNode::run(ExecutionNode*) +{ + m_previousNode= nullptr; + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} +QString StartingNode::toString(bool withlabel) const +{ + if(withlabel) + { + return QString("%1 [label=\"StartingNode\"]").arg(m_id); + } + else + { + return m_id; + } +} + +qint64 StartingNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* StartingNode::getCopy() const +{ + StartingNode* node= new StartingNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/startingnode.h b/src/libparser/node/startingnode.h new file mode 100644 index 0000000..eea72a9 --- /dev/null +++ b/src/libparser/node/startingnode.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 STARTINGNODE_H +#define STARTINGNODE_H + +#include "executionnode.h" + +/** + * @brief The StartingNode class is an ExecutionNode, StartingNode is dedicated to be the first node + * in the execution tree. + */ +class StartingNode : public ExecutionNode +{ +public: + /** + * @brief StartingNode + */ + StartingNode(); + /** + * @brief run + */ + virtual void run(ExecutionNode*); + /** + * @brief toString + * @return + */ + virtual QString toString(bool withlabel) const; + /** + * @brief getPriority + * @return + */ + virtual qint64 getPriority() const; + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; +}; + +#endif // STARTINGNODE_H diff --git a/src/libparser/node/stringnode.cpp b/src/libparser/node/stringnode.cpp new file mode 100644 index 0000000..e908463 --- /dev/null +++ b/src/libparser/node/stringnode.cpp @@ -0,0 +1,58 @@ +#include "stringnode.h" + +StringNode::StringNode() : m_stringResult(new StringResult()) +{ + m_result= m_stringResult; +} + +void StringNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + m_result->setPrevious(previous->getResult()); + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +void StringNode::setString(QString str) +{ + m_data= str; + m_stringResult->addText(m_data); + m_stringResult->finished(); +} +QString StringNode::toString(bool withLabel) const +{ + if(withLabel) + { + QString dataCopy= m_data; + + return QString("%1 [label=\"StringNode %2\"]").arg(m_id, dataCopy.replace('%', '\\')); + } + else + { + return m_id; + } +} +qint64 StringNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* StringNode::getCopy() const +{ + StringNode* node= new StringNode(); + node->setString(m_data); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/stringnode.h b/src/libparser/node/stringnode.h new file mode 100644 index 0000000..4079b7f --- /dev/null +++ b/src/libparser/node/stringnode.h @@ -0,0 +1,29 @@ +#ifndef STRINGNODE_H +#define STRINGNODE_H + +#include "node/executionnode.h" +#include "result/stringresult.h" + +/** + * @brief The StringNode class is an ExecutionNode. It is dedicated to store string and display result. + */ +class StringNode : public ExecutionNode +{ +public: + StringNode(); + void run(ExecutionNode* previous); + void setString(QString str); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const; + +private: + QString m_data; + StringResult* m_stringResult; +}; + +#endif // STRINGNODE_H diff --git a/src/libparser/node/switchcasenode.cpp b/src/libparser/node/switchcasenode.cpp new file mode 100644 index 0000000..880fa4d --- /dev/null +++ b/src/libparser/node/switchcasenode.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + * 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 "stringresult.h" +#include <QDebug> +#include <diceparser/parsingtoolbox.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 Switch/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 Switch/Case operator")); + return; + } + + auto diceResult= dynamic_cast<DiceResult*>(previousResult); + + QSet<QString> alreadyValidDice; + QStringList finalResultList; + if(diceResult) + { + for(auto die : diceResult->getResultList()) + { + QStringList resultList; + for(auto const& info : qAsConst(m_branchList)) + { + if(m_stopAtFirst && !resultList.isEmpty()) + break; + if(info->validatorList) + { + if(info->validatorList->hasValid(die, true)) + { + auto lastNode= ParsingToolBox::getLeafNode(info->node); + if(lastNode && lastNode->getResult()) + { + resultList << lastNode->getResult()->getStringResult(); + } + } + } + else if(resultList.isEmpty()) + { + info->node->run(m_previousNode); + auto lastNode= ParsingToolBox::getLeafNode(info->node); + if(lastNode && lastNode->getResult()) + { + resultList << lastNode->getResult()->getStringResult(); + } + else + resultList << QString(); + } + } + finalResultList << resultList; + } + } + for(auto const& str : qAsConst(finalResultList)) + m_stringResult->addText(str); + + if(m_stringResult->getText().isEmpty()) + m_errors.insert(Dice::ERROR_CODE::NO_VALID_RESULT, QStringLiteral("No value fits the Switch/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<Dice::CaseInfo> info(new Dice::CaseInfo{validator, node}); + m_branchList.push_back(std::move(info)); +} diff --git a/src/libparser/node/switchcasenode.h b/src/libparser/node/switchcasenode.h new file mode 100644 index 0000000..a0f658d --- /dev/null +++ b/src/libparser/node/switchcasenode.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * 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 <vector> + +#include "executionnode.h" +#include "validatorlist.h" + +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<Dice::CaseInfo>> m_branchList; + StringResult* m_stringResult; + bool m_stopAtFirst= false; +}; + +#endif // SWITCHCASENODE_H diff --git a/src/libparser/node/uniquenode.cpp b/src/libparser/node/uniquenode.cpp new file mode 100644 index 0000000..c4668be --- /dev/null +++ b/src/libparser/node/uniquenode.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "uniquenode.h" + +UniqueNode::UniqueNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} +void UniqueNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if(nullptr != previous) + { + m_result->setPrevious(previous->getResult()); + Result* tmpResult= previous->getResult(); + if(nullptr != tmpResult) + { + DiceResult* dice= dynamic_cast<DiceResult*>(tmpResult); + if(nullptr != dice) + { + auto const& resultList= dice->getResultList(); + std::vector<qint64> formerValues; + formerValues.reserve(resultList.size()); + for(auto& oldDie : resultList) + { + auto value= oldDie->getValue(); + auto it= std::find(formerValues.begin(), formerValues.end(), value); + + if(it == formerValues.end()) + { + auto die= new Die(*oldDie); + m_diceResult->insertResult(die); + formerValues.push_back(value); + } + oldDie->displayed(); + } + } + } + } + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +QString UniqueNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"UniqueNode Node\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 UniqueNode::getPriority() const +{ + qint64 priority= 0; + if(nullptr != m_nextNode) + { + priority= m_nextNode->getPriority(); + } + return priority; +} +ExecutionNode* UniqueNode::getCopy() const +{ + UniqueNode* node= new UniqueNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} diff --git a/src/libparser/node/uniquenode.h b/src/libparser/node/uniquenode.h new file mode 100644 index 0000000..039ba11 --- /dev/null +++ b/src/libparser/node/uniquenode.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * 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 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 UNIQUENODE_H +#define UNIQUENODE_H + +#include "node/executionnode.h" +#include "result/diceresult.h" + +/** + * @brief The UniqueNode class is an ExecutionNode. It is dedicated to unique result of one dice into one dimension array. + */ +class UniqueNode : public ExecutionNode +{ +public: + UniqueNode(); + void run(ExecutionNode* previous); + virtual QString toString(bool withLabel) const; + virtual qint64 getPriority() const; + virtual ExecutionNode* getCopy() const; + +private: + DiceResult* m_diceResult; +}; + +#endif // NUMBERNODE_H diff --git a/src/libparser/node/valueslistnode.cpp b/src/libparser/node/valueslistnode.cpp new file mode 100644 index 0000000..33a347d --- /dev/null +++ b/src/libparser/node/valueslistnode.cpp @@ -0,0 +1,62 @@ +#include "valueslistnode.h" + +#include "variablenode.h" + +ValuesListNode::ValuesListNode() : m_diceResult(new DiceResult()) +{ + m_result= m_diceResult; +} + +void ValuesListNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + for(auto node : m_data) + { + node->run(this); + auto result= node->getResult(); + if(!result) + continue; + auto val= result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + Die* die= new Die(); + auto dyna= dynamic_cast<VariableNode*>(node); + if(nullptr != dyna) + dyna->setDisplayed(); + die->insertRollValue(val); + m_diceResult->insertResult(die); + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } +} + +void ValuesListNode::insertValue(ExecutionNode* value) +{ + m_data.push_back(value); +} +ExecutionNode* ValuesListNode::getCopy() const +{ + ValuesListNode* node= new ValuesListNode(); + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} +QString ValuesListNode::toString(bool wl) const +{ + if(wl) + { + return QString("%1 [label=\"ValuesListNode list:\"]").arg(m_id); + } + else + { + return m_id; + } +} +qint64 ValuesListNode::getPriority() const +{ + qint64 priority= 4; + return priority; +} diff --git a/src/libparser/node/valueslistnode.h b/src/libparser/node/valueslistnode.h new file mode 100644 index 0000000..100f275 --- /dev/null +++ b/src/libparser/node/valueslistnode.h @@ -0,0 +1,24 @@ +#ifndef VALUESLISTNODE_H +#define VALUESLISTNODE_H + +#include "executionnode.h" +#include "result/diceresult.h" + +class ValuesListNode : public ExecutionNode +{ +public: + ValuesListNode(); + + virtual void run(ExecutionNode* previous= nullptr) override; + virtual QString toString(bool) const override; + virtual qint64 getPriority() const override; + virtual ExecutionNode* getCopy() const override; + + void insertValue(ExecutionNode*); + +private: + std::vector<ExecutionNode*> m_data; + DiceResult* m_diceResult = nullptr; +}; + +#endif // VALUESLISTNODE_H diff --git a/src/libparser/node/variablenode.cpp b/src/libparser/node/variablenode.cpp new file mode 100644 index 0000000..709ab46 --- /dev/null +++ b/src/libparser/node/variablenode.cpp @@ -0,0 +1,112 @@ +#include "variablenode.h" + +#include "diceresult.h" +#include <diceparser/parsingtoolbox.h> + +VariableNode::VariableNode() {} + +void VariableNode::run(ExecutionNode* previous) +{ + m_previousNode= previous; + if((nullptr != m_data) && (m_data->size() > m_index)) + { + auto value= (*m_data)[m_index]; + value= ParsingToolBox::getLeafNode(value); + if(nullptr == value) + return; + + auto result= value->getResult(); + if(!result) + return; + + m_result= result->getCopy(); + auto diceResult= dynamic_cast<DiceResult*>(result); + if(nullptr != diceResult) + { + for(auto& die : diceResult->getResultList()) + { + die->setDisplayed(false); + } + } + + if(nullptr != m_nextNode) + { + m_nextNode->run(this); + } + } + else + { + m_errors.insert(Dice::ERROR_CODE::NO_VARIBALE, QObject::tr("No variable at index:%1").arg(m_index + 1)); + } +} + +void VariableNode::setDisplayed() +{ + if(!m_result) + return; + auto diceResult= dynamic_cast<DiceResult*>(m_result); + if(nullptr == diceResult) + return; + + for(auto& die : diceResult->getResultList()) + { + die->setDisplayed(true); + } +} + +QString VariableNode::toString(bool withLabel) const +{ + if(withLabel) + { + return QString("%1 [label=\"VariableNode index: %2\"]").arg(m_id).arg(m_index + 1); + } + else + { + return m_id; + } +} + +qint64 VariableNode::getPriority() const +{ + qint64 priority= 4; + if(nullptr != m_previousNode) + { + priority= m_previousNode->getPriority(); + } + return priority; +} + +ExecutionNode* VariableNode::getCopy() const +{ + VariableNode* node= new VariableNode(); + node->setIndex(m_index); + if(nullptr != m_data) + { + node->setData(m_data); + } + if(nullptr != m_nextNode) + { + node->setNextNode(m_nextNode->getCopy()); + } + return node; +} + +quint64 VariableNode::getIndex() const +{ + return m_index; +} + +void VariableNode::setIndex(quint64 index) +{ + m_index= index; +} + +std::vector<ExecutionNode*>* VariableNode::getData() const +{ + return m_data; +} + +void VariableNode::setData(std::vector<ExecutionNode*>* data) +{ + m_data= data; +} diff --git a/src/libparser/node/variablenode.h b/src/libparser/node/variablenode.h new file mode 100644 index 0000000..8bf1cb1 --- /dev/null +++ b/src/libparser/node/variablenode.h @@ -0,0 +1,35 @@ +#ifndef VARIABLENODE_H +#define VARIABLENODE_H + +#include "node/executionnode.h" + +/** + * @brief The VariableNode class is an ExecutionNode. It is dedicated to retrive + * variable value from other starting node. + */ +class VariableNode : public ExecutionNode +{ +public: + VariableNode(); + void run(ExecutionNode* previous) override; + virtual QString toString(bool withLabel) const override; + virtual qint64 getPriority() const override; + /** + * @brief getCopy + * @return + */ + virtual ExecutionNode* getCopy() const override; + quint64 getIndex() const; + void setIndex(quint64 index); + + std::vector<ExecutionNode*>* getData() const; + void setData(std::vector<ExecutionNode*>* data); + + void setDisplayed(); + +private: + quint64 m_index; + std::vector<ExecutionNode*>* m_data= nullptr; +}; + +#endif // VARIABLENODE_H diff --git a/src/libparser/operationcondition.cpp b/src/libparser/operationcondition.cpp new file mode 100644 index 0000000..70ebab4 --- /dev/null +++ b/src/libparser/operationcondition.cpp @@ -0,0 +1,167 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 "operationcondition.h" + +OperationCondition::OperationCondition() + : m_operator(Dice::ConditionOperator::Modulo), m_boolean(nullptr), m_value(nullptr) +{ +} +OperationCondition::~OperationCondition() +{ + if(m_value != nullptr) + { + delete m_value; + m_value= nullptr; + } +} +BooleanCondition* OperationCondition::getBoolean() const +{ + return m_boolean; +} + +void OperationCondition::setBoolean(BooleanCondition* boolean) +{ + m_boolean= boolean; +} + +qint64 OperationCondition::hasValid(Die* b, bool recursive, bool unhighlight) const +{ + if(nullptr == m_boolean) + { + return 0; + } + QList<qint64> listValues; + if(recursive) + { + listValues= b->getListValue(); + } + else + { + listValues.append(b->getLastRolledValue()); + } + + qint64 sum= 0; + for(qint64& value : listValues) + { + switch(m_operator) + { + case Dice::ConditionOperator::Modulo: + { + Die die; + die.setMaxValue(b->getMaxValue()); + auto valueScalar= valueToScalar(); + if(valueScalar == 0) + valueScalar= 1; + die.insertRollValue(value % valueScalar); + sum+= m_boolean->hasValid(&die, recursive, false) ? 1 : 0; + } + break; + } + } + if((unhighlight) && (sum == 0)) + { + b->setHighlighted(false); + } + else + { + b->setHighlighted(true); + } + + return sum; +} + +void OperationCondition::setOperator(Dice::ConditionOperator m) +{ + m_operator= m; +} + +void OperationCondition::setValueNode(ExecutionNode* node) +{ + m_value= node; +} + +QString OperationCondition::toString() +{ + QString str(""); + switch(m_operator) + { + case Dice::ConditionOperator::Modulo: + str.append(QStringLiteral("\\%")); + break; + } + return QStringLiteral("[%1%2%3]").arg(str).arg(valueToScalar()).arg(m_boolean->toString()); +} +Dice::CONDITION_STATE OperationCondition::isValidRangeSize(const std::pair<qint64, qint64>& range) const +{ + Dice::CONDITION_STATE valid= Dice::CONDITION_STATE::REACHABLE; + + auto rangeIsClose= (range.first == range.second); + + Die die; + die.insertRollValue(range.first); + + if(nullptr == m_boolean) + return Dice::CONDITION_STATE::ERROR_STATE; + + if(rangeIsClose && m_boolean->hasValid(&die, false, false)) + valid= Dice::CONDITION_STATE::ALWAYSTRUE; + else if(rangeIsClose && !m_boolean->hasValid(&die, false, false)) + valid= Dice::CONDITION_STATE::UNREACHABLE; + + return valid; +} + +Validator* OperationCondition::getCopy() const +{ + OperationCondition* val= new OperationCondition(); + val->setOperator(m_operator); + val->setValueNode(m_value->getCopy()); + BooleanCondition* boolean= dynamic_cast<BooleanCondition*>(m_boolean->getCopy()); + val->setBoolean(boolean); + return val; +} + +qint64 OperationCondition::valueToScalar() const +{ + if(m_value == nullptr) + return 0; + + m_value->run(nullptr); + auto result= m_value->getResult(); + return result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); +} + +const std::set<qint64>& OperationCondition::getPossibleValues(const std::pair<qint64, qint64>& range) +{ + if(nullptr == m_boolean) + return m_values; + + for(qint64 i= std::min(range.first, range.second); i <= std::max(range.first, range.second); ++i) + { + auto valueScalar= valueToScalar(); + auto val= i % valueScalar; + Die die; + die.insertRollValue(val); + if(m_boolean->hasValid(&die, false, false)) + m_values.insert(i); + } + return m_values; +} diff --git a/src/libparser/operationcondition.h b/src/libparser/operationcondition.h new file mode 100644 index 0000000..719624f --- /dev/null +++ b/src/libparser/operationcondition.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2015 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * rolisteam 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 OPERATIONCONDITION_H +#define OPERATIONCONDITION_H + +#include "booleancondition.h" +#include "node/executionnode.h" +#include "validator.h" +#include <Qt> + +class OperationCondition : public Validator +{ +public: + OperationCondition(); + virtual ~OperationCondition() override; + + virtual qint64 hasValid(Die* b, bool recursive, bool unhighlight= false) const override; + + void setOperator(Dice::ConditionOperator m); + // void setValue(qint64); + void setValueNode(ExecutionNode* node); + QString toString() override; + + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const override; + + BooleanCondition* getBoolean() const; + void setBoolean(BooleanCondition* boolean); + + virtual Validator* getCopy() const override; + + const std::set<qint64>& getPossibleValues(const std::pair<qint64, qint64>& range) override; + +private: + qint64 valueToScalar() const; + +private: + Dice::ConditionOperator m_operator{Dice::ConditionOperator::Modulo}; + BooleanCondition* m_boolean= nullptr; + // qint64 m_value; + ExecutionNode* m_value= nullptr; +}; + +#endif // OPERATIONCONDITION_H diff --git a/src/libparser/parsingtoolbox.cpp b/src/libparser/parsingtoolbox.cpp new file mode 100644 index 0000000..dfde37f --- /dev/null +++ b/src/libparser/parsingtoolbox.cpp @@ -0,0 +1,2641 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "diceparser/parsingtoolbox.h" + +#include <QDebug> +#include <QJsonArray> +#include <QJsonObject> +#include <QRegularExpression> +#include <QString> +#include <set> + +#include "node/allsamenode.h" +#include "node/bind.h" +#include "node/countexecutenode.h" +#include "node/dicerollernode.h" +#include "node/executionnode.h" +#include "node/explodedicenode.h" +#include "node/filternode.h" +#include "node/groupnode.h" +#include "node/helpnode.h" +#include "node/ifnode.h" +#include "node/jumpbackwardnode.h" +#include "node/keepdiceexecnode.h" +#include "node/listaliasnode.h" +#include "node/listsetrollnode.h" +#include "node/mergenode.h" +#include "node/numbernode.h" +#include "node/occurencecountnode.h" +#include "node/paintnode.h" +#include "node/parenthesesnode.h" +#include "node/repeaternode.h" +#include "node/replacevaluenode.h" +#include "node/rerolldicenode.h" +#include "node/scalaroperatornode.h" +#include "node/sortresult.h" +#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" +#include "operationcondition.h" +#include "range.h" +#include "validatorlist.h" + +QHash<QString, QString> ParsingToolBox::m_variableHash; + +ParsingToolBox::ParsingToolBox() +{ + // m_logicOp = ; + m_logicOp.insert(">=", Dice::CompareOperator::GreaterOrEqual); + m_logicOp.insert("<=", Dice::CompareOperator::LesserOrEqual); + m_logicOp.insert("<", Dice::CompareOperator::LesserThan); + m_logicOp.insert("=", Dice::CompareOperator::Equal); + m_logicOp.insert(">", Dice::CompareOperator::GreaterThan); + m_logicOp.insert("!=", Dice::CompareOperator::Different); + + // m_logicOperation = ; + m_logicOperation.insert("|", Dice::LogicOperation::OR); + m_logicOperation.insert("^", Dice::LogicOperation::EXCLUSIVE_OR); + m_logicOperation.insert("&", Dice::LogicOperation::AND); + + // m_conditionOperation = ; + m_conditionOperation.insert("%", Dice::ConditionOperator::Modulo); + + // m_arithmeticOperation = new QHash<QString,ScalarOperatorNode::ArithmeticOperator>(); + m_arithmeticOperation.push_back({QStringLiteral("**"), Dice::ArithmeticOperator::POW}); + m_arithmeticOperation.push_back({QStringLiteral("+"), Dice::ArithmeticOperator::PLUS}); + m_arithmeticOperation.push_back({QStringLiteral("-"), Dice::ArithmeticOperator::MINUS}); + m_arithmeticOperation.push_back({QStringLiteral("*"), Dice::ArithmeticOperator::MULTIPLICATION}); + m_arithmeticOperation.push_back({QStringLiteral("x"), Dice::ArithmeticOperator::MULTIPLICATION}); + m_arithmeticOperation.push_back({QStringLiteral("|"), Dice::ArithmeticOperator::INTEGER_DIVIDE}); + m_arithmeticOperation.push_back({QStringLiteral("/"), Dice::ArithmeticOperator::DIVIDE}); + m_arithmeticOperation.push_back({QStringLiteral("÷"), Dice::ArithmeticOperator::DIVIDE}); + + m_mapDiceOp.insert(QStringLiteral("D"), D); + m_mapDiceOp.insert(QStringLiteral("L"), L); + + m_OptionOp.insert(QStringLiteral("k"), Keep); + m_OptionOp.insert(QStringLiteral("K"), KeepAndExplode); + m_OptionOp.insert(QStringLiteral("s"), Sort); + m_OptionOp.insert(QStringLiteral("c"), Count); + m_OptionOp.insert(QStringLiteral("r"), Reroll); + m_OptionOp.insert(QStringLiteral("e"), Explode); + m_OptionOp.insert(QStringLiteral("R"), RerollUntil); + m_OptionOp.insert(QStringLiteral("a"), RerollAndAdd); + m_OptionOp.insert(QStringLiteral("m"), Merge); + m_OptionOp.insert(QStringLiteral("i"), ifOperator); + m_OptionOp.insert(QStringLiteral("p"), Painter); + m_OptionOp.insert(QStringLiteral("f"), Filter); + m_OptionOp.insert(QStringLiteral("y"), Split); + m_OptionOp.insert(QStringLiteral("u"), Unique); + m_OptionOp.insert(QStringLiteral("t"), AllSameExplode); + 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_OptionOp.insert(QStringLiteral("T"), TransformOption); + + m_functionMap.insert({QStringLiteral("repeat"), REPEAT}); + + m_nodeActionMap.insert(QStringLiteral("@"), JumpBackward); + + m_commandList.append(QStringLiteral("help")); + m_commandList.append(QStringLiteral("la")); +} + +ParsingToolBox::ParsingToolBox(const ParsingToolBox&) {} +ParsingToolBox::~ParsingToolBox() {} + +void ParsingToolBox::clearUp() +{ + m_errorMap.clear(); + m_comment= QString(""); +} + +void ParsingToolBox::cleanUpAliases() +{ + m_aliasList.clear(); +} + +ExecutionNode* ParsingToolBox::addSort(ExecutionNode* e, bool b) +{ + SortResultNode* nodeSort= new SortResultNode(); + nodeSort->setSortAscending(b); + if(nullptr != e) + e->setNextNode(nodeSort); + return nodeSort; +} + +void ParsingToolBox::addError(Dice::ERROR_CODE code, const QString& msg) +{ + m_errorMap.insert(code, msg); +} +void ParsingToolBox::addWarning(Dice::ERROR_CODE code, const QString& msg) +{ + m_warningMap.insert(code, msg); +} +bool ParsingToolBox::readDiceLogicOperator(QString& str, Dice::ConditionOperator& op) +{ + QString longKey; + auto const& keys= m_conditionOperation.keys(); + for(const QString& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_conditionOperation.value(longKey); + return true; + } + + return false; +} + +bool ParsingToolBox::readArithmeticOperator(QString& str, Dice::ArithmeticOperator& op) +{ + + auto it= std::find_if(m_arithmeticOperation.begin(), m_arithmeticOperation.end(), + [str](const std::pair<QString, Dice::ArithmeticOperator>& pair) + { return str.startsWith(pair.first); }); + if(it == m_arithmeticOperation.end()) + return false; + + op= it->second; + str= str.remove(0, it->first.size()); + return true; +} + +bool ParsingToolBox::readLogicOperator(QString& str, Dice::CompareOperator& op) +{ + QString longKey; + auto const& keys= m_logicOp.keys(); + for(const QString& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_logicOp.value(longKey); + return true; + } + + return false; +} +QString ParsingToolBox::getComment() const +{ + return m_comment; +} + +void ParsingToolBox::setComment(const QString& comment) +{ + m_comment= comment; +} +const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getErrorList() const +{ + return m_errorMap; +} +const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getWarningList() const +{ + return m_warningMap; +} +bool ParsingToolBox::readOperand(QString& str, ExecutionNode*& node) +{ + qint64 intValue= 1; + QString resultStr; + if(readDynamicVariable(str, intValue)) + { + VariableNode* variableNode= new VariableNode(); + variableNode->setIndex(static_cast<quint64>(intValue - 1)); + variableNode->setData(&m_startNodes); + node= variableNode; + return true; + } + else if(readNumber(str, intValue)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(intValue); + node= numberNode; + return true; + } + else if(readString(str, resultStr)) + { + StringNode* strNode= new StringNode(); + strNode->setString(resultStr); + node= strNode; + return true; + } + return false; +} + +Validator* ParsingToolBox::readValidator(QString& str, bool hasSquare) +{ + Validator* returnVal= nullptr; + auto opCompare= readConditionType(str); + Dice::CompareOperator myLogicOp{Dice::CompareOperator::Equal}; + readLogicOperator(str, myLogicOp); + + Dice::ConditionOperator condiOp{Dice::ConditionOperator::Modulo}; + bool hasDiceLogicOperator= readDiceLogicOperator(str, condiOp); + ExecutionNode* operandNode= nullptr; + if(hasDiceLogicOperator) + { + if(readOperand(str, operandNode)) + { + OperationCondition* condition= new OperationCondition(); + condition->setConditionType(opCompare); + condition->setValueNode(operandNode); + Validator* valid= readValidator(str, hasSquare); + BooleanCondition* boolC= dynamic_cast<BooleanCondition*>(valid); + if(nullptr != boolC) + { + condition->setBoolean(boolC); + returnVal= condition; + } + else + { + delete condition; + } + } + } + else if(readOperand(str, operandNode)) + { + bool isRange= false; + if(str.startsWith("..") && hasSquare) + { + str= str.remove(0, 2); + qint64 end= 0; + if(readNumber(str, end)) + { + str= str.remove(0, 1); + qint64 start= operandNode->getScalarResult(); + Range* range= new Range(); + range->setConditionType(opCompare); + range->setValue(start, end); + returnVal= range; + isRange= true; + } + } + + if(!isRange) + { + BooleanCondition* condition= new BooleanCondition(); + condition->setConditionType(opCompare); + condition->setValueNode(operandNode); + condition->setOperator(myLogicOp); + returnVal= condition; + } + } + return returnVal; +} + +Dice::ConditionType ParsingToolBox::readConditionType(QString& str) +{ + Dice::ConditionType type= Dice::OnEach; + if(str.startsWith('.')) + { + str= str.remove(0, 1); + type= Dice::OneOfThem; + } + else if(str.startsWith('?')) + { + str= str.remove(0, 1); + type= Dice::OnEachValue; + } + else if(str.startsWith('*')) + { + str= str.remove(0, 1); + type= Dice::AllOfThem; + } + else if(str.startsWith(':')) + { + str= str.remove(0, 1); + type= Dice::OnScalar; + } + return type; +} +bool ParsingToolBox::hasError() const +{ + return !m_errorMap.isEmpty(); +} +ValidatorList* ParsingToolBox::readValidatorList(QString& str) +{ + bool expectSquareBrasket= false; + if((str.startsWith("["))) + { + str= str.remove(0, 1); + expectSquareBrasket= true; + } + Validator* tmp= readValidator(str, expectSquareBrasket); + Dice::LogicOperation opLogic; + + QVector<Dice::LogicOperation> operators; + QList<Validator*> validatorList; + + while(nullptr != tmp) + { + bool hasOperator= readLogicOperation(str, opLogic); + if(hasOperator) + { + operators.append(opLogic); + validatorList.append(tmp); + tmp= readValidator(str, expectSquareBrasket); + } + else + { + if((expectSquareBrasket) && (str.startsWith("]"))) + { + str= str.remove(0, 1); + // isOk=true; + } + validatorList.append(tmp); + tmp= nullptr; + } + } + if(!validatorList.isEmpty()) + { + ValidatorList* validator= new ValidatorList(); + validator->setOperationList(operators); + validator->setValidators(validatorList); + return validator; + } + else + { + return nullptr; + } +} +bool ParsingToolBox::readLogicOperation(QString& str, Dice::LogicOperation& op) +{ + QString longKey; + auto const& keys= m_logicOperation.keys(); + for(auto& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_logicOperation.value(longKey); + return true; + } + + return false; +} + +bool ParsingToolBox::readNumber(QString& str, qint64& myNumber) +{ + if(str.isEmpty()) + return false; + + QString number; + int i= 0; + while(i < str.length() && ((str[i].isNumber()) || ((i == 0) && (str[i] == '-')))) + { + number+= str[i]; + ++i; + } + + if(number.isEmpty()) + { + QString reason; + return readVariable(str, myNumber, reason); + } + + bool ok; + myNumber= number.toLongLong(&ok); + if(ok) + { + str= str.remove(0, number.size()); + return true; + } + + return false; +} +bool ParsingToolBox::readDynamicVariable(QString& str, qint64& index) +{ + if(str.isEmpty()) + return false; + if(str.startsWith('$')) + { + QString number; + int i= 1; + while(i < str.length() && (str[i].isNumber())) + { + number+= str[i]; + ++i; + } + + bool ok; + index= number.toLongLong(&ok); + if(ok) + { + str= str.remove(0, number.size() + 1); + return true; + } + } + return false; +} + +ExecutionNode* ParsingToolBox::getLeafNode(ExecutionNode* start) +{ + if(nullptr == start) + return nullptr; + + ExecutionNode* next= start; + while(nullptr != next->getNextNode()) + { + next= next->getNextNode(); + } + return next; +} + +const std::vector<ExecutionNode*>& ParsingToolBox::getStartNodes() +{ + return m_startNodes; +} + +QStringList ParsingToolBox::allFirstResultAsString(bool& hasAlias) const +{ + // QStringList allResult; + QStringList stringListResult; + for(auto node : m_startNodes) + { + QVariant var; + auto stringPair= hasResultOfType(Dice::RESULT_TYPE::STRING, node); + auto scalarPair= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node); + if(stringPair.first) + { + stringListResult << stringPair.second.toString(); + hasAlias= true; + } + else if(scalarPair.first) + { + stringListResult << number(scalarPair.second.toReal()); + hasAlias= true; + } + } + return stringListResult; +} +std::pair<bool, QVariant> ParsingToolBox::hasResultOfType(Dice::RESULT_TYPE type, ExecutionNode* node, + bool notthelast) const +{ + bool hasValidResult= false; + QVariant var; + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + while((result != nullptr) && (!hasValidResult)) + { + bool lastResult= false; + if(notthelast) + lastResult= (nullptr == result->getPrevious()); + + if(result->hasResultOfType(type) && !lastResult) + { + hasValidResult= true; + var= result->getResult(type); + } + result= result->getPrevious(); + } + return {hasValidResult, var}; +} + +QList<qreal> ParsingToolBox::scalarResultsFromEachInstruction() const +{ + QList<qreal> resultValues; + std::set<QString> alreadyVisitedNode; + for(auto node : m_startNodes) + { + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + bool scalarDone= false; + while((result != nullptr) && (!scalarDone)) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::SCALAR)) + { + if(alreadyVisitedNode.find(result->getId()) == alreadyVisitedNode.end()) + { + resultValues << result->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + alreadyVisitedNode.insert(result->getId()); + } + scalarDone= true; + } + result= result->getPrevious(); + } + } + return resultValues; +} + +QList<qreal> ParsingToolBox::sumOfDiceResult() const +{ + QList<qreal> resultValues; + for(auto node : m_startNodes) + { + qreal resultValue= 0; + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + bool found= false; + while((nullptr != result) && (!found)) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* myDiceResult= dynamic_cast<DiceResult*>(result); + if(nullptr != myDiceResult) + { + for(auto& die : myDiceResult->getResultList()) + { + resultValue+= die->getValue(); + } + found= true; + } + } + result= result->getPrevious(); + } + resultValues << resultValue; + } + return resultValues; +} + +std::pair<QString, QString> ParsingToolBox::finalScalarResult() const +{ + QString scalarText; + QString lastScalarText; + auto listDie= diceResultFromEachInstruction(); + if(hasIntegerResultNotInFirst()) + { + QStringList strLst; + auto listScalar= scalarResultsFromEachInstruction(); + for(auto val : listScalar) + { + strLst << number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); + lastScalarText= strLst.last(); + } + else if(!listDie.isEmpty()) + { + auto values= sumOfDiceResult(); + QStringList strLst; + for(auto val : values) + { + strLst << number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); + } + return {scalarText, lastScalarText}; +} + +bool ParsingToolBox::hasIntegerResultNotInFirst() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node, true).first; + } + return result; +} + +bool ParsingToolBox::hasDiceResult() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::DICE_LIST, node).first; + } + return result; +} +bool ParsingToolBox::hasStringResult() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::STRING, node).first; + } + return result; +} + +QList<ExportedDiceResult> ParsingToolBox::diceResultFromEachInstruction() const +{ + QList<ExportedDiceResult> resultList; + for(auto start : m_startNodes) + { + resultList.append(ParsingToolBox::finalDiceResultFromInstruction(start)); + } + return resultList; +} + +QStringList listOfDiceResult(const QList<ExportedDiceResult>& list, bool removeDouble= false) +{ + QStringList listOfDiceResult; + std::set<QString> alreadyAdded; + for(auto map : list) + { + for(auto key : map.keys()) + { + auto listOfList= map.value(key); + for(auto dice : listOfList) + { + QString stringVal; + for(auto val : dice) + { + if(removeDouble && (alreadyAdded.end() != alreadyAdded.find(val.uuid()))) + continue; + + alreadyAdded.insert(val.uuid()); + + qint64 total= 0; + QStringList dicelist; + for(auto score : val.result()) + { + total+= score; + dicelist << QString::number(score); + } + if(val.result().size() > 1) + { + stringVal= QString("%1 [%2]").arg(total).arg(dicelist.join(',')); + listOfDiceResult << stringVal; + } + else + { + listOfDiceResult << QString::number(total); + } + } + } + } + } + return listOfDiceResult; +} + +QString ParsingToolBox::finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize, + bool removeUnhighlighted) const +{ + bool ok; + QStringList allStringlist= allFirstResultAsString(ok); + auto listFull= diceResultFromEachInstruction(); + + QStringList resultWithPlaceHolder; + std::for_each(allStringlist.begin(), allStringlist.end(), + [&resultWithPlaceHolder](const QString& sub) + { + QRegularExpression ex("%[1-3]?|\\$[1-9]+|@[1-9]+"); + if(sub.contains(ex)) + resultWithPlaceHolder.append(sub); + }); + auto stringResult= resultWithPlaceHolder.isEmpty() ? allStringlist.join(",") : resultWithPlaceHolder.join(","); + + auto pairScalar= finalScalarResult(); + + stringResult.replace("%1", pairScalar.first); + stringResult.replace("%2", listOfDiceResult(diceResultFromEachInstruction(), true).join(",").trimmed()); + stringResult.replace("%3", pairScalar.second); + stringResult.replace("\\n", "\n"); + + QMap<Dice::ERROR_CODE, QString> errorMap; + stringResult= ParsingToolBox::replaceVariableToValue(stringResult, allStringlist, errorMap); + stringResult= ParsingToolBox::replacePlaceHolderToValue(stringResult, listFull, removeUnhighlighted, colorize); + + return stringResult; +} + +bool ParsingToolBox::readString(QString& str, QString& strResult) +{ + if(str.isEmpty()) + return false; + + if(str.startsWith('"')) + { + str= str.remove(0, 1); + + int i= 0; + int j= 0; + bool previousEscape= false; + QString result; + /*&& + (((!previousEscape) && !(str[i]=='"')) || (previousEscape) && !(str[i]=='"')) + || (str[i]=='\\'))*/ + while(i < str.length() && (!(!previousEscape && (str[i] == '"')) || (previousEscape && str[i] != '"'))) + { + if(str[i] == '\\') + { + previousEscape= true; + } + else + { + if(previousEscape && str[i] != '\"') + { + result+= '\\'; + ++j; + } + result+= str[i]; + previousEscape= false; + } + ++i; + } + + if(!result.isEmpty()) + { + str= str.remove(0, i); + strResult= result; + if(str.startsWith('"')) + { + str= str.remove(0, 1); + return true; + } + } + } + + return false; +} + +bool ParsingToolBox::readVariable(QString& str, qint64& myNumber, QString& reasonFail) +{ + if(str.isEmpty()) + return false; + + if(str.startsWith("${")) + { + str= str.remove(0, 2); + } + QString key; + int post= str.indexOf('}'); + key= str.left(post); + + if(!m_variableHash.isEmpty()) + { + if(m_variableHash.contains(key)) + { + QString value= m_variableHash.value(key); + bool ok; + int valueInt= value.toInt(&ok); + if(ok) + { + myNumber= valueInt; + str= str.remove(0, post + 1); + return true; + } + else + { + reasonFail= QStringLiteral("Variable value is %1, not a number").arg(value); + } + } + else + { + reasonFail= QStringLiteral("Variable not found"); + } + } + else + { + reasonFail= QStringLiteral("No Variables are defined"); + } + + return false; +} +bool ParsingToolBox::readComma(QString& str) +{ + if(str.startsWith(",")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} +bool ParsingToolBox::readOpenParentheses(QString& str) +{ + if(str.startsWith("(")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} + +bool ParsingToolBox::readCloseParentheses(QString& str) +{ + if(str.startsWith(")")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} + +int ParsingToolBox::findClosingCharacterIndexOf(QChar open, QChar closing, const QString& str, int offset) +{ + int counter= offset; + int i= 0; + for(auto const& letter : str) + { + if(letter == open) + ++counter; + else if(letter == closing) + --counter; + + if(counter == 0) + return i; + + ++i; + } + return -1; +} + +bool ParsingToolBox::readList(QString& str, QStringList& list, QList<Range>& ranges) +{ + if(str.startsWith("[")) + { + str= str.remove(0, 1); + int pos= findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]"); + if(-1 != pos) + { + QString liststr= str.left(pos); + list= liststr.split(","); + str= str.remove(0, pos + 1); + readProbability(list, ranges); + return true; + } + } + return false; +} +bool ParsingToolBox::readAscending(QString& str) +{ + if(str.isEmpty()) + { + return false; + } + else if(str.at(0) == 'l') + { + str= str.remove(0, 1); + return true; + } + 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); + if(nullptr == node) + return Dice::CONDITION_STATE::ERROR_STATE; + + return val->isValidRangeSize(node->getRange()); +} +DiceRollerNode* ParsingToolBox::getDiceRollerNode(ExecutionNode* previous) +{ + while(nullptr != previous) + { + DiceRollerNode* node= dynamic_cast<DiceRollerNode*>(previous); + if(nullptr != node) + { + return node; + } + previous= previous->getPreviousNode(); + } + return nullptr; +} +bool ParsingToolBox::readDiceRange(QString& str, qint64& start, qint64& end) +{ + bool expectSquareBrasket= false; + + if((str.startsWith("["))) + { + str= str.remove(0, 1); + expectSquareBrasket= true; + } + if(readNumber(str, start)) + { + if(str.startsWith("..")) + { + str= str.remove(0, 2); + if(readNumber(str, end)) + { + if(expectSquareBrasket) + { + if(str.startsWith("]")) + { + str= str.remove(0, 1); + return true; + } + } + } + } + else if(expectSquareBrasket) + { + if(str.startsWith("]")) + { + str= str.remove(0, 1); + end= start; + return true; + } + } + } + return false; +} +ParsingToolBox::LIST_OPERATOR ParsingToolBox::readListOperator(QString& str) +{ + QHash<QChar, ParsingToolBox::LIST_OPERATOR> hash; + hash.insert('u', UNIQUE); + hash.insert('n', NOCOMMA); + bool findOne= false; + ParsingToolBox::LIST_OPERATOR op= NONE; + int i= 0; + do + { + auto keys= hash.keys(); + findOne= false; + for(auto const& key : qAsConst(keys)) + { + if(str.startsWith(key)) + { + str= str.remove(0, 1); + op= hash[key]; + findOne= true; + ++i; + } + } + } while(findOne); + + return i > 1 ? UniqueAndNoComma : op; +} + +bool ParsingToolBox::readPainterParameter(PainterNode* painter, QString& str) +{ + if(!str.startsWith('[')) + return false; + + str= str.remove(0, 1); + int pos= str.indexOf(']'); + + if(pos == -1) + return false; + + QString data= str.left(pos); + str= str.remove(0, pos + 1); + QStringList duos= data.split(','); + bool result= false; + for(QString& duoStr : duos) + { + QStringList keyValu= duoStr.split(':'); + if(keyValu.size() == 2) + { + painter->insertColorItem(keyValu[1], keyValu[0].toInt()); + result= true; + } + } + + return result; +} + +QHash<QString, QString> ParsingToolBox::getVariableHash() +{ + return m_variableHash; +} + +void ParsingToolBox::setVariableHash(const QHash<QString, QString>& variableHash) +{ + m_variableHash= variableHash; +} + +void ParsingToolBox::setStartNodes(std::vector<ExecutionNode*> nodes) +{ + m_startNodes= nodes; +} + +void ParsingToolBox::readProbability(QStringList& str, QList<Range>& ranges) +{ + quint64 totalDistance= 0; + quint64 undefDistance= 0; + int undefCount= 0; + int maxValue= 0; + int i= 0; + int j= 0; + bool hasPercentage= false; + for(QString line : str) + { + int pos= line.indexOf('['); + if(-1 != pos) + { + QString rangeStr= line.right(line.length() - pos); + line= line.left(pos); + str[j]= line; + qint64 start= 0; + qint64 end= 0; + if(readDiceRange(rangeStr, start, end)) + { + Range range; + range.setValue(start, end); + ranges.append(range); + totalDistance+= static_cast<quint64>(end - start + 1); + ++i; + } + else // percentage + { + hasPercentage= true; + Range range; + range.setStart(start); + ranges.append(range); + ++undefCount; + undefDistance+= static_cast<quint64>(start); + } + if((end > maxValue) || (i == 1)) + { + maxValue= static_cast<int>(end); + } + } + else + { + Range range; + range.setEmptyRange(true); + ranges.append(range); + } + ++j; + } + + if((hasPercentage) && (undefDistance != 0)) + { + qreal ratio= 100.0 / static_cast<qreal>(undefDistance); + qint64 realStart= 0; + for(int i= 0; i < ranges.size(); ++i) + { + Range tmp= ranges.at(i); + if(!tmp.isFullyDefined()) + { + int dist= static_cast<int>(tmp.getStart()); + tmp.setStart(realStart + 1); + double truc= dist * ratio; + + tmp.setEnd(static_cast<int>(realStart + truc)); + realStart= tmp.getEnd(); + ranges[i]= tmp; + } + } + } + else + { + int limitUp= 1; + for(int i= 0; i < ranges.size(); ++i) + { + Range range= ranges.at(i); + if(range.isEmptyRange()) + { + range.setStart(limitUp); + range.setEnd(limitUp); + range.setEmptyRange(false); + } + else + { + qint64 sizeRange= range.getEnd() - range.getStart(); + range.setStart(limitUp); + limitUp+= sizeRange; + range.setEnd(limitUp); + } + ++limitUp; + ranges[i]= range; + } + } +} +bool ParsingToolBox::readComment(QString& str, QString& result, QString& comment) +{ + QString left= str; + str= str.trimmed(); + if(str.startsWith("#")) + { + comment= left; + str= str.remove(0, 1); + result= str.trimmed(); + str= ""; + return true; + } + return false; +} + +QString ParsingToolBox::replaceVariableToValue(const QString& source, QStringList values, + QMap<Dice::ERROR_CODE, QString>& errorMap) +{ + QString result= source; + + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readVariableFromString(source, start); + if(ref.resultIndex() > values.size()) + { + auto error= QString("No valid value at index: $%1").arg(ref.resultIndex()); + errorMap.insert(Dice::ERROR_CODE::INVALID_INDEX, error); + return error; + } + + valid= ref.isValid(); + if(!valid) + continue; + + result.remove(ref.position(), ref.length()); + auto val= values[ref.resultIndex() - 1]; + + if(ref.subIndex() >= 0) + { + auto valSplit= val.split(","); + if(ref.subIndex() < valSplit.size()) + val= valSplit[ref.subIndex()]; + } + + if(ref.digitNumber() != 0) + { + auto realVal= QString("%1").arg(val, ref.digitNumber(), QChar('0')); + result.insert(ref.position(), realVal); + } + else + { + result.insert(ref.position(), val); + } + } while(valid); + + return result; +} + +QString ParsingToolBox::replacePlaceHolderFromJson(const QString& source, const QJsonObject& obj) +{ + QStringList resultList; + auto instructions= obj["instructions"].toArray(); + std::vector<std::vector<std::pair<int, QList<QStringList>>>> instructionResult; + for(auto inst : qAsConst(instructions)) + { + std::vector<std::pair<int, QList<QStringList>>> map; + auto obj= inst.toObject(); + auto vals= obj["diceval"].toArray(); + int lastFace= -1; + for(auto const& valRef : qAsConst(vals)) + { + auto diceObj= valRef.toObject(); + auto face= diceObj["face"].toInt(); + auto it= std::find_if(std::begin(map), std::end(map), + [face](const std::pair<int, QList<QStringList>>& val) { return val.first == face; }); + + auto realVal= diceObj["string"].toString(); + if(lastFace == -1 || lastFace != face) + { + QList<QStringList> listOfList; + listOfList << (QStringList() << realVal); + map.push_back({face, listOfList}); + } + else if(lastFace == face) + { + auto& valList= it->second.last(); + valList.append(realVal); + } + lastFace= face; + } + instructionResult.push_back(map); + } + std::transform(std::begin(instructionResult), std::end(instructionResult), std::back_inserter(resultList), + [](const std::vector<std::pair<int, QList<QStringList>>>& map) + { + QStringList valuesStr; + auto multiKey= (map.size() > 1); + for(auto item : map) + { + auto face= item.first; + auto valueList= item.second; + QStringList strs; + for(auto list : valueList) + { + strs << list.join(","); + } + if(!multiKey) + valuesStr << strs.join(","); + else + valuesStr << QString("d%1:(%2)").arg(face).arg(strs.join(",")); + } + return valuesStr.join(" - "); + }); + + QString result= source; + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readPlaceHolderFromString(source, start); + if(ref.isValid()) + { + result.remove(ref.position(), ref.length()); + auto val= resultList[ref.resultIndex() - 1]; + result.insert(ref.position(), val); + } + else + { + valid= false; + } + } while(valid); + + return result; +} + +QString ParsingToolBox::replacePlaceHolderToValue(const QString& source, const QList<ExportedDiceResult>& list, + bool removeUnhighlighted, + std::function<QString(const QString&, const QString&, bool)> colorize) +{ + QStringList resultList; + std::transform( + std::begin(list), std::end(list), std::back_inserter(resultList), + [removeUnhighlighted, colorize](const ExportedDiceResult& dice) + { + QStringList valuesStr; + if(dice.size() == 1) + { + auto values= dice.values(); + std::transform( + std::begin(values), std::end(values), std::back_inserter(valuesStr), + [removeUnhighlighted, colorize](const QList<ListDiceResult>& dice) + { + QStringList textList; + std::transform( + std::begin(dice), std::end(dice), std::back_inserter(textList), + [removeUnhighlighted, colorize](const ListDiceResult& dice) + { + QStringList list; + ListDiceResult values= dice; + if(removeUnhighlighted) + { + values.clear(); + std::copy_if(std::begin(dice), std::end(dice), std::back_inserter(values), + [](const HighLightDice& hl) { return hl.isHighlighted(); }); + } + + std::transform(std::begin(values), std::end(values), std::back_inserter(list), + [colorize](const HighLightDice& hl) + { return colorize(hl.getResultString(), {}, hl.isHighlighted()); }); + return list.join(","); + }); + textList.removeAll(QString()); + return textList.join(","); + }); + } + else if(dice.size() > 1) + { + for(auto key : dice.keys()) + { + auto list= dice.value(key); + for(auto values : list) + { + QStringList textVals; + std::transform(std::begin(values), std::end(values), std::back_inserter(textVals), + [](const HighLightDice& dice) { return dice.getResultString(); }); + valuesStr.append(QString("d%1 [%2]").arg(key).arg(textVals.join(","))); + } + } + } + return valuesStr.join(","); + }); + + QString result= source; + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readPlaceHolderFromString(source, start); + if(ref.isValid()) + { + result.remove(ref.position(), ref.length()); + auto val= resultList[ref.resultIndex() - 1]; + result.insert(ref.position(), val); + } + else + { + valid= false; + } + } while(valid); + + return result; +} +void ParsingToolBox::readSubtitutionParameters(SubtituteInfo& info, QString& rest) +{ + auto sizeS= rest.size(); + if(rest.startsWith("{")) + { + rest= rest.remove(0, 1); + qint64 number; + if(readNumber(rest, number)) + { + if(rest.startsWith("}")) + { + rest= rest.remove(0, 1); + info.setDigitNumber(static_cast<int>(number)); + } + } + } + if(rest.startsWith("[")) + { + rest= rest.remove(0, 1); + qint64 number; + if(readNumber(rest, number)) + { + if(rest.startsWith("]")) + { + rest= rest.remove(0, 1); + info.setSubIndex(static_cast<int>(number)); + } + } + } + info.setLength(info.length() + sizeS - rest.size()); +} + +bool ParsingToolBox::readReaperArguments(RepeaterNode* node, QString& source) +{ + if(!readOpenParentheses(source)) + return false; + + auto instructions= readInstructionList(source, false); + if(instructions.empty()) + return false; + + readComma(source); + ExecutionNode* tmp; + if(readOperand(source, tmp)) + { + if(source.startsWith("+")) + { + node->setSumAll(true); + source= source.remove(0, 1); + } + if(readCloseParentheses(source)) + { + node->setCommand(instructions); + node->setTimeNode(tmp); + return true; + } + } + + return false; +} +bool ParsingToolBox::readExpression(QString& str, ExecutionNode*& node) +{ + ExecutionNode* operandNode= nullptr; + if(readOpenParentheses(str)) + { + ExecutionNode* internalNode= nullptr; + if(readExpression(str, internalNode)) + { + ParenthesesNode* parentheseNode= new ParenthesesNode(); + parentheseNode->setInternelNode(internalNode); + node= parentheseNode; + if(readCloseParentheses(str)) + { + ExecutionNode* diceNode= nullptr; + ExecutionNode* operatorNode= nullptr; + if(readDice(str, diceNode)) + { + parentheseNode->setNextNode(diceNode); + } + else if(readExpression(str, operatorNode)) + { + parentheseNode->setNextNode(operatorNode); + } + return true; + } + else + { + m_warningMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Expected closing parenthesis - can't validate the inside.")); + } + } + } + else if(readFunction(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOptionFromNull(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOperatorFromNull(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOperand(str, operandNode)) + { + ExecutionNode* diceNode= nullptr; + if(readDice(str, diceNode)) + { + operandNode->setNextNode(diceNode); + } + node= operandNode; + + operandNode= ParsingToolBox::getLeafNode(operandNode); + // ExecutionNode* operatorNode=nullptr; + while(readOperator(str, operandNode)) + { + // operandNode->setNextNode(operatorNode); + operandNode= ParsingToolBox::getLeafNode(operandNode); + } + return true; + } + else if(readCommand(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readNode(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readValuesList(str, operandNode)) + { + node= operandNode; + return true; + } + else + { + ExecutionNode* diceNode= nullptr; + if(readDice(str, diceNode)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(1); + numberNode->setNextNode(diceNode); + node= numberNode; + return true; + } + else + { + return false; + } + } + return false; +} + +bool ParsingToolBox::readValuesList(QString& str, ExecutionNode*& node) +{ + if(!str.startsWith("[")) + return false; + + str= str.remove(0, 1); + int pos= ParsingToolBox::findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]"); + if(-1 == pos) + return false; + + QString liststr= str.left(pos); + auto list= liststr.split(","); + str= str.remove(0, pos + 1); + auto values= new ValuesListNode(); + for(auto var : qAsConst(list)) + { + qint64 number= 1; + var= var.trimmed(); + if(ParsingToolBox::readDynamicVariable(var, number)) + { + VariableNode* variableNode= new VariableNode(); + variableNode->setIndex(static_cast<quint64>(number - 1)); + variableNode->setData(&m_startNodes); + values->insertValue(variableNode); + } + else if(ParsingToolBox::readNumber(var, number)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(number); + values->insertValue(numberNode); + } + } + node= values; + return true; +} +bool ParsingToolBox::readOptionFromNull(QString& str, ExecutionNode*& node) +{ + StartingNode nodePrevious; + if(readOption(str, &nodePrevious)) + { + auto nodeNext= nodePrevious.getNextNode(); + nodePrevious.setNextNode(nullptr); + node= nodeNext; + return true; + } + return false; +} + +void ParsingToolBox::setHelpPath(const QString& path) +{ + m_helpPath= path; +} + +bool ParsingToolBox::readOperatorFromNull(QString& str, ExecutionNode*& node) +{ + StartingNode nodePrevious; + if(readOperator(str, &nodePrevious)) + { + auto nodeNext= nodePrevious.getNextNode(); + nodePrevious.setNextNode(nullptr); + node= nodeNext; + return true; + } + return false; +} + +bool ParsingToolBox::readOption(QString& str, ExecutionNode* previous) //, +{ + if(str.isEmpty()) + { + return false; + } + + bool found= false; + auto keys= m_OptionOp.keys(); + for(int i= 0; ((i < keys.size()) && (!found)); ++i) + { + QString key= keys.at(i); + + if(str.startsWith(key)) + { + str= str.remove(0, key.size()); + auto operatorName= m_OptionOp.value(key); + switch(operatorName) + { + case Keep: + { + ExecutionNode* value= nullptr; + bool ascending= readAscending(str); + + if(readOperand(str, value)) + { + auto node= addSort(previous, ascending); + KeepDiceExecNode* nodeK= new KeepDiceExecNode(); + nodeK->setDiceKeepNumber(value); + node->setNextNode(nodeK); + found= true; + } + } + break; + case KeepAndExplode: + { + bool ascending= readAscending(str); + ExecutionNode* value= nullptr; + if(readOperand(str, value)) + { + DiceRollerNode* nodeTmp= dynamic_cast<DiceRollerNode*>(previous); + if(nullptr != nodeTmp) + { + previous= addExplodeDiceNode(static_cast<qint64>(nodeTmp->getFaces()), previous); + } + + auto node= addSort(previous, ascending); + + KeepDiceExecNode* nodeK= new KeepDiceExecNode(); + nodeK->setDiceKeepNumber(value); + + node->setNextNode(nodeK); + node= nodeK; + found= true; + } + } + break; + case Filter: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + auto validity= isValidValidator(previous, validatorList); + + FilterNode* filterNode= new FilterNode(); + filterNode->setValidatorList(validatorList); + + previous->setNextNode(filterNode); + found= true; + } + } + break; + case Sort: + { + bool ascending= readAscending(str); + addSort(previous, ascending); + /*if(!hasDice) + { + m_errorMap.insert(ExecutionNode::BAD_SYNTAXE,QObject::tr("Sort Operator does not support default + dice. You should add dice command before the s")); + }*/ + found= true; + } + break; + case Count: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + auto validity= isValidValidator(previous, validatorList); + + CountExecuteNode* countNode= new CountExecuteNode(); + countNode->setValidatorList(validatorList); + + previous->setNextNode(countNode); + found= true; + } + else + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the c operator. Please, change it")); + } + } + break; + case Reroll: + case RerollUntil: + case RerollAndAdd: + // Todo: I think that Exploding and Rerolling could share the same code + { + auto validatorList= readValidatorList(str); + QString symbol= m_OptionOp.key(operatorName); + if(nullptr != validatorList) + { + switch(isValidValidator(previous, validatorList)) + { + case Dice::CONDITION_STATE::ALWAYSTRUE: + if(operatorName == RerollAndAdd) + { + m_errorMap.insert( + Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Validator is always true for the %1 operator. Please, change it") + .arg(symbol)); + } + break; + case Dice::CONDITION_STATE::UNREACHABLE: + if(operatorName == RerollUntil) + { + m_errorMap.insert( + Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Condition can't be reached, causing endless loop. Please, " + "change the %1 option condition") + .arg(symbol)); + } + break; + case Dice::CONDITION_STATE::ERROR_STATE: + default: + break; + } + + auto reroll= (operatorName == RerollAndAdd || operatorName == Reroll); + auto addingMode= (operatorName == RerollAndAdd); + RerollDiceNode* rerollNode= new RerollDiceNode(reroll, addingMode); + ExecutionNode* nodeParam= nullptr; + if(readParameterNode(str, nodeParam)) + { + rerollNode->setInstruction(nodeParam); + } + rerollNode->setValidatorList(validatorList); + previous->setNextNode(rerollNode); + found= true; + } + else + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the %1 operator. Please, change it").arg(symbol)); + } + } + break; + case Explode: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + if(Dice::CONDITION_STATE::ALWAYSTRUE == isValidValidator(previous, validatorList)) + { + m_errorMap.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("This condition %1 introduces an endless loop. Please, change it") + .arg(validatorList->toString())); + } + ExplodeDiceNode* explodedNode= new ExplodeDiceNode(); + explodedNode->setValidatorList(validatorList); + previous->setNextNode(explodedNode); + found= true; + } + else + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the e operator. Please, change it")); + } + } + break; + case Merge: + { + MergeNode* mergeNode= new MergeNode(); + mergeNode->setStartList(&m_startNodes); + previous->setNextNode(mergeNode); + found= true; + } + break; + case AllSameExplode: + { + AllSameNode* allSame= new AllSameNode(); + previous->setNextNode(allSame); + found= true; + } + break; + case SwitchCaseOption: + { + auto scNode= new SwitchCaseNode(); + found= readSwitchCaseNode(str, scNode); + previous->setNextNode(scNode); + } + break; + case TransformOption: + { + auto scNode= new ReplaceValueNode(); + found= readReplaceValueNode(str, scNode); + previous->setNextNode(scNode); + } + break; + case Bind: + { + BindNode* bindNode= new BindNode(); + bindNode->setStartList(&m_startNodes); + previous->setNextNode(bindNode); + found= true; + } + break; + case Occurences: + { + qint64 number= 0; + auto occNode= new OccurenceCountNode(); + if(readNumber(str, number)) + { + occNode->setWidth(number); + auto validatorList= readValidatorList(str); + if(validatorList) + { + occNode->setValidatorList(validatorList); + } + else if(readComma(str)) + { + if(readNumber(str, number)) + { + occNode->setHeight(number); + } + } + } + previous->setNextNode(occNode); + found= true; + } + break; + case Unique: + { + auto node= new UniqueNode(); + previous->setNextNode(node); + found= true; + } + break; + case Painter: + { + PainterNode* painter= new PainterNode(); + if(!readPainterParameter(painter, str)) + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Missing parameter for Painter node (p)")); + delete painter; + } + else + { + previous->setNextNode(painter); + found= true; + } + } + break; + case ifOperator: + { + IfNode* nodeif= new IfNode(); + nodeif->setConditionType(readConditionType(str)); + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + ExecutionNode* trueNode= nullptr; + ExecutionNode* falseNode= nullptr; + if(readIfInstruction(str, trueNode, falseNode)) + { + nodeif->setInstructionTrue(trueNode); + nodeif->setInstructionFalse(falseNode); + nodeif->setValidatorList(validatorList); + previous->setNextNode(nodeif); + found= true; + } + else + { + delete nodeif; + } + } + else + { + delete nodeif; + } + break; + } + case Split: + { + SplitNode* splitnode= new SplitNode(); + previous->setNextNode(splitnode); + found= true; + } + break; + case Group: + { + bool stringResult= readStringResultParameter(str); + qint64 groupNumber= 0; + if(readNumber(str, groupNumber)) + { + GroupNode* groupNode= new GroupNode(stringResult); + groupNode->setGroupValue(groupNumber); + previous->setNextNode(groupNode); + found= true; + } + } + break; + } + } + } + return found; +} +bool ParsingToolBox::readStringResultParameter(QString& str) +{ + if(str.startsWith("s")) + { + str.remove(0, 1); + return true; + } + return false; +} +bool ParsingToolBox::readIfInstruction(QString& str, ExecutionNode*& trueNode, ExecutionNode*& falseNode) +{ + if(readBlocInstruction(str, trueNode)) + { + if(readBlocInstruction(str, falseNode)) + { + return true; + } + return true; + } + return false; +} +DiceRollerNode* ParsingToolBox::addRollDiceNode(qint64 faces, ExecutionNode* previous) +{ + DiceRollerNode* mydiceRoller= new DiceRollerNode(faces); + previous->setNextNode(mydiceRoller); + return mydiceRoller; +} +ExplodeDiceNode* ParsingToolBox::addExplodeDiceNode(qint64 value, ExecutionNode* previous) +{ + ExplodeDiceNode* explodeDiceNode= new ExplodeDiceNode(); + NumberNode* node= new NumberNode(); + node->setNumber(value); + BooleanCondition* condition= new BooleanCondition(); + condition->setConditionType(Dice::OnEach); + condition->setValueNode(node); + condition->setOperator(Dice::CompareOperator::Equal); + auto valList= new ValidatorList(); + valList->setValidators(QList<Validator*>() << condition); + auto validity= isValidValidator(previous, valList); + explodeDiceNode->setValidatorList(valList); + previous->setNextNode(explodeDiceNode); + return explodeDiceNode; +} +bool ParsingToolBox::readParameterNode(QString& str, ExecutionNode*& node) +{ + if(str.startsWith("(")) + { + str= str.remove(0, 1); + if(readExpression(str, node)) + { + if(str.startsWith(")")) + { + str= str.remove(0, 1); + return true; + } + } + } + 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::readReplaceValueNode(QString& str, ReplaceValueNode* node) +{ + bool res= false; + 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('{')) + { + str= str.remove(0, 1); + ExecutionNode* node= nullptr; + Dice::ArithmeticOperator op; + ScalarOperatorNode* scalarNode= nullptr; + if(readArithmeticOperator(str, op)) + { + scalarNode= new ScalarOperatorNode(); + scalarNode->setArithmeticOperator(op); + } + if(readExpression(str, node)) + { + if(str.startsWith('}')) + { + if(nullptr == scalarNode) + { + resultnode= node; + } + else + { + resultnode= scalarNode; + scalarNode->setInternalNode(node); + } + str= str.remove(0, 1); + return true; + } + } + } + return false; +} +bool ParsingToolBox::readDice(QString& str, ExecutionNode*& node) +{ + DiceOperator currentOperator; + + if(readDiceOperator(str, currentOperator)) + { + if(currentOperator == D) + { + qint64 max; + qint64 min; + bool unique= (ParsingToolBox::UNIQUE == readListOperator(str)) ? true : false; + Dice::ArithmeticOperator op; + + bool hasOp= readArithmeticOperator(str, op); + if(readNumber(str, max)) + { + if(max < 1) + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Dice with %1 face(s) does not exist. Please, put a value higher than 0").arg(max)); + return false; + } + DiceRollerNode* drNode= new DiceRollerNode(max); + drNode->setUnique(unique); + if(hasOp) + { + drNode->setOperator(op); + } + node= drNode; + ExecutionNode* current= drNode; + while(readOption(str, current)) + { + current= ParsingToolBox::getLeafNode(current); + } + return true; + } + else if(readDiceRange(str, min, max)) + { + DiceRollerNode* drNode= new DiceRollerNode(max, min); + drNode->setUnique(unique); + if(hasOp) + { + drNode->setOperator(op); + } + node= drNode; + ExecutionNode* current= drNode; + while(readOption(str, current)) + { + current= ParsingToolBox::getLeafNode(current); + } + return true; + } + } + else if(currentOperator == L) + { + QStringList list; + QList<Range> listRange; + ParsingToolBox::LIST_OPERATOR op= readListOperator(str); + if(readList(str, list, listRange)) + { + ListSetRollNode* lsrNode= new ListSetRollNode(); + lsrNode->setRangeList(listRange); + if(op == ParsingToolBox::UNIQUE || op == ParsingToolBox::UniqueAndNoComma) + { + lsrNode->setUnique(true); + } + if(op == ParsingToolBox::NOCOMMA || op == ParsingToolBox::UniqueAndNoComma) + { + lsrNode->setNoComma(true); + } + lsrNode->setListValue(list); + node= lsrNode; + return true; + } + else + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr( + "List is missing after the L operator. Please, add it (e.g : 1L[sword,spear,gun,arrow])")); + } + } + } + + return false; +} +bool ParsingToolBox::readDiceOperator(QString& str, DiceOperator& op) +{ + QStringList listKey= m_mapDiceOp.keys(); + for(const QString& key : qAsConst(listKey)) + { + if(str.startsWith(key, Qt::CaseInsensitive)) + { + str= str.remove(0, key.size()); + op= m_mapDiceOp.value(key); + return true; + } + } + return false; +} +QString ParsingToolBox::convertAlias(QString str) +{ + for(auto& cmd : m_aliasList) + { + if(cmd->isEnable()) + { + cmd->resolved(str); + } + } + return str; +} + +bool ParsingToolBox::readCommand(QString& str, ExecutionNode*& node) +{ + if(m_commandList.contains(str)) + { + if(str == QLatin1String("help")) + { + str= str.remove(0, QLatin1String("help").size()); + HelpNode* help= new HelpNode(); + if(!m_helpPath.isEmpty()) + { + help->setHelpPath(m_helpPath); + } + node= help; + } + else if(str == QLatin1String("la")) + { + str= str.remove(0, QLatin1String("la").size()); + node= new ListAliasNode(m_aliasList); + } + return true; + } + return false; +} + +bool ParsingToolBox::readDiceExpression(QString& str, ExecutionNode*& node) +{ + bool returnVal= false; + + ExecutionNode* next= nullptr; + if(readDice(str, next)) + { + ExecutionNode* latest= next; + while(readOption(str, latest)) + { + while(nullptr != latest->getNextNode()) + { + latest= latest->getNextNode(); + } + } + + node= next; + returnVal= true; + } + else + { + returnVal= false; + } + return returnVal; +} + +bool ParsingToolBox::readOperator(QString& str, ExecutionNode* previous) +{ + bool result= false; + if(str.isEmpty() || nullptr == previous) + { + return result; + } + + Dice::ArithmeticOperator op; + if(readArithmeticOperator(str, op)) + { + ScalarOperatorNode* node= new ScalarOperatorNode(); + node->setArithmeticOperator(op); + ExecutionNode* nodeExec= nullptr; + if(readExpression(str, nodeExec)) + { + node->setInternalNode(nodeExec); + if(nullptr == nodeExec) + { + delete node; + return result; + } + ExecutionNode* nodeExecOrChild= nodeExec; + ExecutionNode* parent= nullptr; + + while((nullptr != nodeExecOrChild) && (node->getPriority() < nodeExecOrChild->getPriority())) + { + parent= nodeExecOrChild; + nodeExecOrChild= nodeExecOrChild->getNextNode(); + } + // management of operator priority + if((nullptr != nodeExecOrChild) && (nodeExec != nodeExecOrChild)) + { + // good 1 1 2 ; bad 1 0 4 + if(nodeExecOrChild->getPriority() >= node->getPriority()) + { + node->setNextNode(nodeExecOrChild); + parent->setNextNode(nullptr); + } + } + else if(node->getPriority() >= nodeExec->getPriority()) + { + node->setNextNode(nodeExec->getNextNode()); + nodeExec->setNextNode(nullptr); + } + + // nodeResult = node; + previous->setNextNode(node); + + result= true; + } + else + { + delete node; + } + } + else + { + while(readOption(str, previous)) + { + previous= ParsingToolBox::getLeafNode(previous); + result= true; + } + } + return result; +} +bool ParsingToolBox::readFunction(QString& str, ExecutionNode*& node) +{ + for(const auto& kv : m_functionMap) + { + if(str.startsWith(kv.first)) + { + str= str.remove(0, kv.first.size()); + switch(kv.second) + { + case REPEAT: + { + auto repeaterNode= new RepeaterNode(); + if(ParsingToolBox::readReaperArguments(repeaterNode, str)) + { + node= repeaterNode; + } + } + break; + } + } + } + + if(node == nullptr) + return false; + return true; +} + +bool ParsingToolBox::readNode(QString& str, ExecutionNode*& node) +{ + if(str.isEmpty()) + return false; + + QString key= str.at(0); + if(m_nodeActionMap.contains(key)) + { + JumpBackwardNode* jumpNode= new JumpBackwardNode(); + node= jumpNode; + str= str.remove(0, 1); + readOption(str, jumpNode); + return true; + } + return false; +} + +bool ParsingToolBox::readInstructionOperator(QChar c) +{ + if(c == ';') + { + return true; + } + return false; +} + +void ParsingToolBox::insertAlias(DiceAlias* dice, int i) +{ + if(i >= m_aliasList.size()) + { + m_aliasList.insert(i, dice); + } +} +const QList<DiceAlias*>& ParsingToolBox::getAliases() const +{ + return m_aliasList; +} + +QList<DiceAlias*>* ParsingToolBox::aliases() +{ + return &m_aliasList; +} + +void ParsingToolBox::setAliases(const QList<DiceAlias*> list) +{ + qDeleteAll(m_aliasList); + m_aliasList.clear(); + m_aliasList= list; +} + +std::vector<ExecutionNode*> ParsingToolBox::readInstructionList(QString& str, bool global) +{ + if(str.isEmpty()) + return {}; + + std::vector<ExecutionNode*> startNodes; + + bool hasInstruction= false; + bool readInstruction= true; + while(readInstruction) + { + ExecutionNode* startNode= nullptr; + bool keepParsing= readExpression(str, startNode); + if(nullptr != startNode) + { + hasInstruction= true; + startNodes.push_back(startNode); + auto latest= startNode; + if(keepParsing) + { + latest= ParsingToolBox::getLeafNode(latest); + keepParsing= !str.isEmpty(); + while(keepParsing) + { + auto before= str; + if(readOperator(str, latest)) + { + latest= ParsingToolBox::getLeafNode(latest); + } + keepParsing= (!str.isEmpty() && (before != str)); + } + } + if(!str.isEmpty() && readInstructionOperator(str[0])) + { + str= str.remove(0, 1); + } + else + { + QString result; + QString comment; + if(readComment(str, result, comment)) + { + m_comment= result; + } + readInstruction= false; + } + } + else + { + readInstruction= false; + } + } + if(global) + m_startNodes= startNodes; + return startNodes; +} + +SubtituteInfo ParsingToolBox::readVariableFromString(const QString& source, int& start) +{ + bool found= false; + SubtituteInfo info; + int i= start; + for(; i >= 0 && !found; --i) + { + if(source.at(i) == '$') + { + auto rest= source.mid(i + 1, 1 + start - i); + qint64 number; + if(readNumber(rest, number)) + { + auto len= QString::number(number).size() - 1; + readSubtitutionParameters(info, rest); + info.setLength(info.length() + len); + info.setResultIndex(static_cast<int>(number)); + info.setPosition(i); + found= true; + } + } + } + start= i; + return info; +} + +SubtituteInfo ParsingToolBox::readPlaceHolderFromString(const QString& source, int& start) +{ + bool found= false; + SubtituteInfo info; + int i= start; + for(; i >= 0 && !found; --i) + { + if(source.at(i) == '@') + { + auto rest= source.mid(i + 1, 1 + start - i); + qint64 number; + if(readNumber(rest, number)) + { + auto len= QString::number(number).size() - 1; + readSubtitutionParameters(info, rest); + info.setLength(info.length() + len); + info.setResultIndex(static_cast<int>(number)); + info.setPosition(i); + found= true; + } + } + } + start= i; + return info; +} + +QString ParsingToolBox::number(qreal value) +{ + if(value > 1000000) + return QString::number(value, 'f', 20); + else + return QString::number(value); +} + +ExportedDiceResult ParsingToolBox::finalDiceResultFromInstruction(ExecutionNode* start) +{ + ExecutionNode* next= ParsingToolBox::getLeafNode(start); + Result* result= next->getResult(); + ExportedDiceResult nodeResult; + std::set<QString> alreadyAdded; + while(nullptr != result) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* diceResult= dynamic_cast<DiceResult*>(result); + QList<HighLightDice> list; + quint64 faces= 0; + for(auto& die : diceResult->getResultList()) + { + faces= die->getFaces(); + HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(), + die->hasBeenDisplayed(), die->getFaces(), die->getUuid()); + if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end() && !hlDice.displayed()) + { + list.append(hlDice); + alreadyAdded.insert(die->getUuid()); + } + } + if(!list.isEmpty()) + { + auto vals= nodeResult.value(faces); + vals.append(list); + nodeResult.insert(faces, vals); + } + } + /*if(nodeResult.isEmpty()) + result= result->getPrevious(); + else*/ + result= result->getPrevious(); + } + return nodeResult; +} + +ExportedDiceResult ParsingToolBox::allDiceResultFromInstruction(ExecutionNode* start) +{ + ExecutionNode* next= ParsingToolBox::getLeafNode(start); + Result* result= next->getResult(); + ExportedDiceResult nodeResult; + std::set<QString> alreadyAdded; + while(nullptr != result) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* diceResult= dynamic_cast<DiceResult*>(result); + QList<HighLightDice> list; + quint64 faces= 0; + for(auto& die : diceResult->getResultList()) + { + faces= die->getFaces(); + HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(), + die->hasBeenDisplayed(), die->getFaces(), die->getUuid()); + if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end()) + { + list.append(hlDice); + alreadyAdded.insert(die->getUuid()); + } + } + if(!list.isEmpty()) + { + auto vals= nodeResult.value(faces); + vals.append(list); + nodeResult.insert(faces, vals); + } + } + result= result->getPrevious(); + } + return nodeResult; +} + +void ParsingToolBox::addResultInJson(QJsonObject& obj, Dice::RESULT_TYPE type, const QString& key, ExecutionNode* start, + bool b) +{ + auto pair= hasResultOfType(type, start, b); + if(pair.first) + obj[key]= QJsonValue::fromVariant(pair.second); +} + +void ParsingToolBox::addDiceResultInJson( + QJsonObject& obj, ExecutionNode* start, + std::function<QString(const QString& value, const QString& color, bool highlighted)> colorize) +{ + QJsonArray diceValues; + auto result= ParsingToolBox::allDiceResultFromInstruction(start); + for(auto listOfList : result.values()) + { + for(auto listDiceResult : listOfList) + { + for(auto hlDice : listDiceResult) + { + QJsonObject diceObj; + diceObj["face"]= static_cast<qreal>(hlDice.faces()); + diceObj["color"]= hlDice.color(); + diceObj["displayed"]= hlDice.displayed(); + diceObj["string"]= colorize(hlDice.getResultString(), hlDice.color(), hlDice.isHighlighted()); + diceObj["highlight"]= hlDice.isHighlighted(); + diceObj["uuid"]= hlDice.uuid(); + auto val= hlDice.result(); + if(!val.isEmpty()) + { + diceObj["value"]= std::accumulate(val.begin(), val.end(), 0); + if(val.size() > 1) + { + QJsonArray intValues; + std::transform(val.begin(), val.end(), std::back_inserter(intValues), + [](qint64 val) { return static_cast<int>(val); }); + diceObj["subvalues"]= intValues; + } + } + diceValues.append(diceObj); + } + } + } + if(!diceValues.isEmpty()) + obj["diceval"]= diceValues; +} + +SubtituteInfo::SubtituteInfo() {} + +bool SubtituteInfo::isValid() const +{ + return !(m_position + m_resultIndex < 0); +} + +int SubtituteInfo::length() const +{ + return m_length; +} + +void SubtituteInfo::setLength(int length) +{ + m_length= length; +} + +int SubtituteInfo::resultIndex() const +{ + return m_resultIndex; +} + +void SubtituteInfo::setResultIndex(int valueIndex) +{ + m_resultIndex= valueIndex; +} + +int SubtituteInfo::position() const +{ + return m_position; +} + +void SubtituteInfo::setPosition(int position) +{ + m_position= position; +} + +int SubtituteInfo::digitNumber() const +{ + return m_digitNumber; +} + +void SubtituteInfo::setDigitNumber(int digitNumber) +{ + m_digitNumber= digitNumber; +} + +int SubtituteInfo::subIndex() const +{ + return m_subIndex; +} + +void SubtituteInfo::setSubIndex(int subindex) +{ + m_subIndex= subindex; +} diff --git a/src/libparser/qmltypesregister.cpp b/src/libparser/qmltypesregister.cpp new file mode 100644 index 0000000..dbd2ac9 --- /dev/null +++ b/src/libparser/qmltypesregister.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2017 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * 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 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 <diceparser_qobject/qmltypesregister.h> + +#include <QQmlEngine> + +#include <diceparser_qobject/diceroller.h> +#if !defined(UNIT_TEST) && defined(RCSE) +#include "field.h" +#endif + +void registerQmlTypes() +{ + auto p= new DiceRoller; + // Q_UNUSED(p) + qmlRegisterType<DiceRoller>("Rolisteam", 1, 0, "DiceRoller"); +#if !defined(UNIT_TEST) && defined(RCSE) + qmlRegisterType<FieldController>("Rolisteam", 1, 0, "Field"); +#endif + delete p; +} diff --git a/src/libparser/range.cpp b/src/libparser/range.cpp new file mode 100644 index 0000000..39a8f66 --- /dev/null +++ b/src/libparser/range.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "range.h" + +Range::Range() : m_hasEnd(false), m_hasStart(false), m_emptyRange(false) {} +void Range::setValue(qint64 s, qint64 e) +{ + m_start= s; + m_end= e; + + m_hasEnd= true; + m_hasStart= true; +} + +qint64 Range::hasValid(Die* m, bool recursive, bool unhighlight) const +{ + qint64 result= 0; + if(recursive) + { + for(qint64& value : m->getListValue()) + { + if((value >= m_start) && (value <= m_end)) + { + ++result; + } + } + } + else if((m->getLastRolledValue() >= m_start) && (m->getLastRolledValue() <= m_end)) + { + ++result; + } + if((unhighlight) && (result == 0)) + { + m->setHighlighted(false); + } + return result; +} +QString Range::toString() +{ + return QStringLiteral("[%1-%2]").arg(m_start).arg(m_end); +} +Dice::CONDITION_STATE Range::isValidRangeSize(const std::pair<qint64, qint64>& range) const +{ + auto minRange= std::min(m_start, m_end); + auto minPossibleValue= std::min(range.first, range.second); + + auto maxRange= std::max(m_start, m_end); + auto maxPossibleValue= std::max(range.first, range.second); + + if(minRange == minPossibleValue && maxRange == maxPossibleValue) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else if(maxRange < minPossibleValue || minRange > maxPossibleValue) + return Dice::CONDITION_STATE::UNREACHABLE; + else + return Dice::CONDITION_STATE::UNREACHABLE; +} +void Range::setStart(qint64 start) +{ + m_start= start; + m_hasStart= true; +} +void Range::setEnd(qint64 end) +{ + m_end= end; + m_hasEnd= true; +} + +bool Range::isFullyDefined() const +{ + return (m_hasEnd && m_hasStart); +} +qint64 Range::getStart() const +{ + return m_start; +} +qint64 Range::getEnd() const +{ + return m_end; +} +void Range::setEmptyRange(bool b) +{ + m_emptyRange= b; +} + +bool Range::isEmptyRange() +{ + return m_emptyRange; +} +Validator* Range::getCopy() const +{ + Range* val= new Range(); + val->setEmptyRange(m_emptyRange); + if(m_hasEnd) + { + val->setEnd(m_end); + } + if(m_hasStart) + { + val->setStart(m_start); + } + return val; +} diff --git a/src/libparser/range.h b/src/libparser/range.h new file mode 100644 index 0000000..276ed55 --- /dev/null +++ b/src/libparser/range.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 RANGE_H +#define RANGE_H + +#include "validator.h" +#include <Qt> + +/** + * @brief The Range class is validator class to check validity between two values. + */ +class Range : public Validator +{ +public: + Range(); + void setValue(qint64, qint64); + void setStart(qint64); + void setEnd(qint64); + virtual qint64 hasValid(Die* b, bool recursive, bool unlight= false) const override; + + virtual QString toString() override; + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const override; + + bool isFullyDefined() const; + qint64 getStart() const; + qint64 getEnd() const; + + void setEmptyRange(bool); + bool isEmptyRange(); + + virtual Validator* getCopy() const override; + +private: + qint64 m_start= 0; + qint64 m_end= 0; + bool m_hasEnd; + bool m_hasStart; + bool m_emptyRange; +}; + +#endif // RANGE_H diff --git a/src/libparser/result/diceresult.cpp b/src/libparser/result/diceresult.cpp new file mode 100644 index 0000000..31e316b --- /dev/null +++ b/src/libparser/result/diceresult.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "diceresult.h" +#include <QDebug> + +DiceResult::DiceResult() +{ + m_resultTypes= (static_cast<int>(Dice::RESULT_TYPE::DICE_LIST) | static_cast<int>(Dice::RESULT_TYPE::SCALAR)); + m_homogeneous= true; +} +void DiceResult::insertResult(Die* die) +{ + m_diceValues.append(die); +} +QList<Die*>& DiceResult::getResultList() +{ + return m_diceValues; +} +bool DiceResult::isHomogeneous() const +{ + return m_homogeneous; +} +void DiceResult::setHomogeneous(bool b) +{ + m_homogeneous= b; +} + +void DiceResult::setResultList(QList<Die*> list) +{ + m_diceValues.erase( + std::remove_if(m_diceValues.begin(), m_diceValues.end(), [list](Die* die) { return list.contains(die); }), + m_diceValues.end()); + + qDeleteAll(m_diceValues.begin(), m_diceValues.end()); + m_diceValues.clear(); + m_diceValues << list; +} +DiceResult::~DiceResult() +{ + if(!m_diceValues.isEmpty()) + { + qDeleteAll(m_diceValues.begin(), m_diceValues.end()); + m_diceValues.clear(); + } +} +QVariant DiceResult::getResult(Dice::RESULT_TYPE type) +{ + switch(type) + { + case Dice::RESULT_TYPE::SCALAR: + { + return getScalarResult(); + } + case Dice::RESULT_TYPE::DICE_LIST: + { + return QVariant::fromValue(m_diceValues); + } + default: + break; + } + return QVariant(); +} +bool DiceResult::contains(Die* die, const std::function<bool(const Die*, const Die*)> equal) +{ + for(auto& value : m_diceValues) + { + if(equal(value, die)) + { + return true; + } + } + return false; +} +qreal DiceResult::getScalarResult() +{ + if(m_diceValues.size() == 1) + { + return m_diceValues[0]->getValue(); + } + else + { + qint64 scalar= 0; + int i= 0; + for(auto& tmp : m_diceValues) + { + if(i > 0) + { + switch(m_operator) + { + case Dice::ArithmeticOperator::PLUS: + scalar+= tmp->getValue(); + break; + case Dice::ArithmeticOperator::MULTIPLICATION: + scalar*= tmp->getValue(); + break; + case Dice::ArithmeticOperator::MINUS: + scalar-= tmp->getValue(); + break; + case Dice::ArithmeticOperator::POW: + scalar= static_cast<int>(pow(static_cast<double>(scalar), static_cast<double>(tmp->getValue()))); + break; + case Dice::ArithmeticOperator::DIVIDE: + case Dice::ArithmeticOperator::INTEGER_DIVIDE: + if(tmp->getValue() != 0) + { + scalar/= tmp->getValue(); + } + else + { + /// @todo Error cant divide by 0. Must be displayed. + } + break; + } + } + else + { + scalar= tmp->getValue(); + } + ++i; + } + return scalar; + } +} + +Dice::ArithmeticOperator DiceResult::getOperator() const +{ + return m_operator; +} + +void DiceResult::clear() +{ + m_diceValues.clear(); +} + +void DiceResult::setOperator(const Dice::ArithmeticOperator& dieOperator) +{ + m_operator= dieOperator; +} +QString DiceResult::toString(bool wl) +{ + QStringList scalarSum; + for(auto& die : m_diceValues) + { + scalarSum << QString::number(die->getValue()); + } + if(wl) + { + return QStringLiteral("%3 [label=\"DiceResult Value %1 dice %2\"]") + .arg(QString::number(getScalarResult()), scalarSum.join('_'), m_id); + } + else + { + return m_id; + } +} +Result* DiceResult::getCopy() const +{ + auto copy= new DiceResult(); + copy->setHomogeneous(m_homogeneous); + copy->setOperator(m_operator); + copy->m_id= m_id; + QList<Die*> list; + for(auto die : m_diceValues) + { + auto newdie= new Die(*die); + newdie->setDisplayed(false); + // die->displayed(); + list.append(newdie); + } + copy->setResultList(list); + copy->setPrevious(getPrevious()); + return copy; +} diff --git a/src/libparser/result/diceresult.h b/src/libparser/result/diceresult.h new file mode 100644 index 0000000..1b35b7e --- /dev/null +++ b/src/libparser/result/diceresult.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 DICERESULT_H +#define DICERESULT_H +#include <QList> +#include <functional> + +#include "diceparser/diceparserhelper.h" +#include "die.h" +#include "result.h" +/** + * @brief The DiceResult class + */ +class DiceResult : public Result +{ +public: + /** + * @brief DiceResult + */ + DiceResult(); + /** + * @brief ~DiceResult + */ + virtual ~DiceResult() override; + + /** + * @brief getResultList + * @return + */ + virtual QList<Die*>& getResultList(); + /** + * @brief insertResult + */ + virtual void insertResult(Die*); + + /** + * @brief setResultList + * @param list + */ + virtual void setResultList(QList<Die*> list); + + /** + * @brief getScalar + * @return + */ + virtual QVariant getResult(Dice::RESULT_TYPE) override; + /** + * @brief toString + * @return + */ + virtual QString toString(bool wl) override; + /** + * @brief isHomogeneous + */ + virtual bool isHomogeneous() const; + /** + * @brief setHomogeneous + */ + virtual void setHomogeneous(bool); + + Dice::ArithmeticOperator getOperator() const; + void setOperator(const Dice::ArithmeticOperator& dieOperator); + bool contains(Die* die, const std::function<bool(const Die*, const Die*)> equal); + + void clear() override; + + virtual Result* getCopy() const override; + +protected: + qreal getScalarResult(); + +protected: + QList<Die*> m_diceValues; + bool m_homogeneous; + Dice::ArithmeticOperator m_operator{Dice::ArithmeticOperator::PLUS}; +}; +Q_DECLARE_METATYPE(QList<Die*>) +#endif // DICERESULT_H diff --git a/src/libparser/result/result.cpp b/src/libparser/result/result.cpp new file mode 100644 index 0000000..ca410b3 --- /dev/null +++ b/src/libparser/result/result.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "result.h" +#include <QUuid> + +Result::Result() + : m_resultTypes(static_cast<int>(Dice::RESULT_TYPE::NONE)) + , m_id(QString("\"%1\"").arg(QUuid::createUuid().toString())) + , m_previous(nullptr) +{ +} +Result::~Result() {} + +Result* Result::getPrevious() const +{ + return m_previous; +} + +void Result::setPrevious(Result* p) +{ + Q_ASSERT(p != this); + m_previous= p; +} + +bool Result::isStringResult() const +{ + return false; +} +void Result::clear() {} +bool Result::hasResultOfType(Dice::RESULT_TYPE type) const +{ + return (m_resultTypes & static_cast<int>(type)); +} +void Result::generateDotTree(QString& s) +{ + auto str= toString(true); + if(s.contains(str)) + return; + s.append(str); + s.append(";\n"); + + if(nullptr != m_previous) + { + s.append(toString(false)); + s.append(" -> "); + s.append(m_previous->toString(false)); + s.append("[label=\"previousResult\"]\n"); + m_previous->generateDotTree(s); + } + else + { + s.append(toString(false)); + s.append(" -> "); + s.append("nullptr"); + s.append(" [label=\"previousResult\", shape=\"box\"];\n"); + } +} + +QString Result::getId() const +{ + return m_id; +} + +QString Result::getStringResult() const +{ + return {}; +} diff --git a/src/libparser/result/result.h b/src/libparser/result/result.h new file mode 100644 index 0000000..4432682 --- /dev/null +++ b/src/libparser/result/result.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 RESULT_H +#define RESULT_H + +#include <QString> +#include <QVariant> +#include <diceparser/diceparserhelper.h> +/** + * @brief The Result class + */ +class Result +{ +public: + /** + * @brief Result + */ + Result(); + /** + * @brief ~Result + */ + virtual ~Result(); + + /** + * @brief isScalar + * @return + */ + virtual bool hasResultOfType(Dice::RESULT_TYPE) const; + /** + * @brief getScalar + * @return + */ + virtual QVariant getResult(Dice::RESULT_TYPE)= 0; + /** + * @brief getPrevious + * @return + */ + virtual Result* getPrevious() const; + /** + * @brief setPrevious + */ + virtual void setPrevious(Result*); + /** + * @brief isStringResult + * @return + */ + virtual bool isStringResult() const; + + virtual void clear(); + + /** + * @brief getStringResult + * @return + */ + virtual QString getStringResult() const; + /** + * @brief generateDotTree + */ + void generateDotTree(QString&); + /** + * @brief toString + * @return + */ + virtual QString toString(bool wl)= 0; + virtual Result* getCopy() const= 0; + + QString getId() const; + +protected: + int m_resultTypes; /// @brief + QString m_id; + +private: + Result* m_previous= nullptr; /// @brief +}; + +#endif // RESULT_H diff --git a/src/libparser/result/scalarresult.cpp b/src/libparser/result/scalarresult.cpp new file mode 100644 index 0000000..dcbaa61 --- /dev/null +++ b/src/libparser/result/scalarresult.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "scalarresult.h" + +ScalarResult::ScalarResult() +{ + m_resultTypes= static_cast<int>(Dice::RESULT_TYPE::SCALAR); +} + +void ScalarResult::setValue(qreal i) +{ + m_value= i; +} +QVariant ScalarResult::getResult(Dice::RESULT_TYPE type) +{ + if(Dice::RESULT_TYPE::SCALAR == type) + { + return m_value; + } + else + return {}; +} +Result* ScalarResult::getCopy() const +{ + auto copy= new ScalarResult(); + copy->setValue(m_value); + return copy; +} +QString ScalarResult::toString(bool wl) +{ + if(wl) + { + return QString("%2 [label=\"ScalarResult %1\"]").arg(m_value).arg(m_id); + } + else + { + return m_id; + } +} diff --git a/src/libparser/result/scalarresult.h b/src/libparser/result/scalarresult.h new file mode 100644 index 0000000..73fe73b --- /dev/null +++ b/src/libparser/result/scalarresult.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 SCALARRESULT_H +#define SCALARRESULT_H + +#include "result.h" +#include <Qt> + +/** + * @brief The ScalarResult class is used to store scalar result by many ExecutionNode. + */ +class ScalarResult : public Result +{ +public: + /** + * @brief ScalarResult + */ + ScalarResult(); + /** + * @brief getResult + * @return + */ + virtual QVariant getResult(Dice::RESULT_TYPE); + /** + * @brief setValue + * @param i + */ + void setValue(qreal i); + /** + * @brief toString + * @return + */ + virtual QString toString(bool); + virtual Result* getCopy() const; + +private: + qreal m_value= 0; +}; + +#endif // SCALARRESULT_H diff --git a/src/libparser/result/stringresult.cpp b/src/libparser/result/stringresult.cpp new file mode 100644 index 0000000..113c22c --- /dev/null +++ b/src/libparser/result/stringresult.cpp @@ -0,0 +1,117 @@ +#include "stringresult.h" +#include <QDebug> + +StringResult::StringResult() +{ + m_highlight= true; + m_resultTypes= static_cast<int>(Dice::RESULT_TYPE::STRING); +} +void StringResult::addText(QString text) +{ + m_value.append(text); +} +StringResult::~StringResult() {} +bool StringResult::hasResultOfType(Dice::RESULT_TYPE resultType) const +{ + bool val= false; + + switch(resultType) + { + case Dice::RESULT_TYPE::STRING: + val= !isDigitOnly(); + break; + case Dice::RESULT_TYPE::SCALAR: + val= isDigitOnly(); + break; + case Dice::RESULT_TYPE::DICE_LIST: + val= (isDigitOnly() && m_value.size() > 1); + break; + default: + break; + } + return val; +} + +void StringResult::setNoComma(bool b) +{ + m_commaSeparator= !b; +} + +QString StringResult::getText() const +{ + return m_commaSeparator ? m_value.join(",") : m_value.join(QString()); +} + +QVariant StringResult::getResult(Dice::RESULT_TYPE type) +{ + switch(type) + { + case Dice::RESULT_TYPE::STRING: + return getText(); + case Dice::RESULT_TYPE::SCALAR: + return getScalarResult(); + default: + return QVariant(); + } +} +QString StringResult::toString(bool wl) +{ + if(wl) + { + return QString("%2 [label=\"StringResult_value_%1\"]").arg(getText().replace("%", "_"), m_id); + } + else + { + return m_id; + } +} +void StringResult::setHighLight(bool b) +{ + m_highlight= b; +} + +bool StringResult::hasHighLight() const +{ + return m_highlight; +} + +void StringResult::finished() +{ + if(isDigitOnly()) + { + std::for_each(m_value.begin(), m_value.end(), [this](const QString& str) { + auto die= new Die(); + die->setMaxValue(m_stringCount); + die->setValue(str.toInt()); + insertResult(die); + }); + } +} + +void StringResult::setStringCount(int count) +{ + m_stringCount= count; +} + +bool StringResult::isDigitOnly() const +{ + return std::all_of(m_value.begin(), m_value.end(), [](const QString& str) { + bool ok= false; + str.toInt(&ok); + return ok; + }); +} + +Result* StringResult::getCopy() const +{ + auto copy= new StringResult(); + copy->setPrevious(getPrevious()); + copy->setHighLight(m_highlight); + std::for_each(m_value.begin(), m_value.end(), [copy](const QString& str) { copy->addText(str); }); + return copy; +} + +QString StringResult::getStringResult() const +{ + return getText(); +} diff --git a/src/libparser/result/stringresult.h b/src/libparser/result/stringresult.h new file mode 100644 index 0000000..b0ca539 --- /dev/null +++ b/src/libparser/result/stringresult.h @@ -0,0 +1,45 @@ +#ifndef STRINGRESULT_H +#define STRINGRESULT_H + +#include "diceresult.h" +#include <QString> +/** + * @brief The StringResult class stores command result for String. + */ + +class StringResult : public DiceResult +{ +public: + /** + * @brief StringResult + */ + StringResult(); + /** + * @brief StringResult + */ + virtual ~StringResult() override; + void addText(QString text); + void finished(); + QString getText() const; + virtual QVariant getResult(Dice::RESULT_TYPE) override; + virtual QString toString(bool) override; + + virtual void setHighLight(bool); + virtual bool hasHighLight() const; + virtual bool hasResultOfType(Dice::RESULT_TYPE resultType) const override; + virtual Result* getCopy() const override; + + bool isDigitOnly() const; + + void setStringCount(int count); + QString getStringResult() const override; + void setNoComma(bool b); + +private: + QStringList m_value; + bool m_highlight= true; + int m_stringCount= 0; + bool m_commaSeparator= true; +}; + +#endif // STRINGRESULT_H diff --git a/src/libparser/validator.cpp b/src/libparser/validator.cpp new file mode 100644 index 0000000..201574b --- /dev/null +++ b/src/libparser/validator.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "validator.h" + +Validator::Validator() {} +Validator::~Validator() {} + +template <typename Functor> +qint64 Validator::onEach(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + qint64 result= 0; + std::for_each(b.begin(), b.end(), [this, recursive, unlight, functor, &result](Die* die) { + if(hasValid(die, recursive, unlight)) + { + ++result; + functor(die, recursive, unlight); + } + }); + return result; +} + +template <typename Functor> +qint64 Validator::onEachValue(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + qint64 result= 0; + std::for_each(b.begin(), b.end(), [this, recursive, unlight, functor, &result](Die* die) { + if(hasValid(die, recursive, unlight)) + { + ++result; + functor(die, recursive, unlight); + } + }); + return result; +} + +template <typename Functor> +qint64 Validator::oneOfThem(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + auto oneOfThem= std::any_of(b.begin(), b.end(), + [this, recursive, unlight](Die* die) { return hasValid(die, recursive, unlight); }); + if(oneOfThem) + functor(recursive, unlight); + return oneOfThem ? 1 : 0; +} + +template <typename Functor> +qint64 Validator::allOfThem(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + auto all= std::all_of(b.begin(), b.end(), + [this, recursive, unlight](Die* die) { return hasValid(die, recursive, unlight); }); + if(all) + functor(recursive, unlight); + return all ? 1 : 0; +} + +template <typename Functor> +qint64 Validator::onScalar(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + qint64 result= 0; + for(const auto& die : b) + { + result+= die->getValue(); + } + Die die; + die.setValue(result); + if(hasValid(&die, recursive, unlight)) + { + functor(recursive, unlight); + return 1; + } + return 0; +} + +const std::set<qint64>& Validator::getPossibleValues(const std::pair<qint64, qint64>&) +{ + return m_values; +} + +template <typename Functor> +qint64 Validator::validResult(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const +{ + qint64 result; + switch(m_conditionType) + { + case Dice::OnEach: + result= onEach(b, recursive, unlight, functor); + break; + case Dice::OnEachValue: + result= onEachValue(b, recursive, unlight, functor); + break; + case Dice::OneOfThem: + result= oneOfThem(b, recursive, unlight, functor); + break; + case Dice::AllOfThem: + result= allOfThem(b, recursive, unlight, functor); + break; + case Dice::OnScalar: + result= onScalar(b, recursive, unlight, functor); + break; + } + return result; +} + +Dice::ConditionType Validator::getConditionType() const +{ + return m_conditionType; +} + +void Validator::setConditionType(const Dice::ConditionType& conditionType) +{ + m_conditionType= conditionType; +} diff --git a/src/libparser/validator.h b/src/libparser/validator.h new file mode 100644 index 0000000..ca15f67 --- /dev/null +++ b/src/libparser/validator.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 VALIDATOR_H +#define VALIDATOR_H + +#include <diceparser/diceparserhelper.h> + +#include "die.h" +#include <QString> +#include <Qt> +#include <set> +/** + * @brief The Validator class is an abstract class for checking the validity of dice for some + * operator. + */ +// template <Dice::ConditionType C> +class Validator +{ +public: + /** + * @brief Validator + */ + Validator(); + /** + * @brief ~Validator + */ + virtual ~Validator(); + /** + * @brief hasValid + * @param b + * @param recursive + * @param unlight + * @return + */ + virtual qint64 hasValid(Die* b, bool recursive, bool unlight= false) const= 0; + /** + * @brief toString + * @return + */ + virtual QString toString()= 0; + /** + * @brief getValidRangeSize + * @param faces + * @return + */ + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const= 0; + /** + * @brief getCopy + * @return return a copy of this validator + */ + virtual Validator* getCopy() const= 0; + /** + * @brief getPossibleValues + * @param range + * @return + */ + virtual const std::set<qint64>& getPossibleValues(const std::pair<qint64, qint64>& range); + /** + * @brief validResult + * @param b + * @param recursive + * @param unlight + * @return + */ + template <typename Functor> + qint64 validResult(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + + Dice::ConditionType getConditionType() const; + void setConditionType(const Dice::ConditionType& conditionType); + +protected: + template <typename Functor> + qint64 onEach(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + template <typename Functor> + qint64 onEachValue(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + template <typename Functor> + qint64 oneOfThem(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + template <typename Functor> + qint64 allOfThem(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + template <typename Functor> + qint64 onScalar(const std::vector<Die*>& b, bool recursive, bool unlight, Functor functor) const; + +protected: + std::set<qint64> m_values; + Dice::ConditionType m_conditionType= Dice::OnEach; +}; + +#endif // VALIDATOR_H diff --git a/src/libparser/validatorlist.cpp b/src/libparser/validatorlist.cpp new file mode 100644 index 0000000..f1d1ba4 --- /dev/null +++ b/src/libparser/validatorlist.cpp @@ -0,0 +1,453 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 "validatorlist.h" + +#include "result/diceresult.h" +#include "result/result.h" +#include "validator.h" +#include <QDebug> +#include <utility> + +void mergeResultsAsAND(const ValidatorResult& diceList, ValidatorResult& result) +{ + ValidatorResult val; + for(auto dice : diceList.validDice()) + { + if(result.contains(dice.first) || diceList.allTrue()) + val.appendValidDice(dice.first, dice.second); + } + result= val; + result.setAllTrue(diceList.allTrue() & result.allTrue()); +} + +void mergeResultsAsExeclusiveOR(const ValidatorResult& diceList, ValidatorResult& result) +{ + ValidatorResult val; + for(auto dice : diceList.validDice()) + { + if(!result.contains(dice.first)) + val.appendValidDice(dice.first, dice.second); + } + result= val; + result.setAllTrue(diceList.allTrue() ^ result.allTrue()); +} + +DiceResult* getDiceResult(Result* result) +{ + auto dice= dynamic_cast<DiceResult*>(result); + if(nullptr == dice) + { + auto value= result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + dice= new DiceResult(); + auto die= new Die(); + die->setValue(value); + dice->insertResult(die); + qWarning("Error, no dice result"); + // TODO: manage error here. + } + return dice; +} + +////////////////////////////////// +/// \brief ValidatorResult::ValidatorResult +/// +/// +/// +/// //////////////////////////////// +ValidatorResult::ValidatorResult() {} + +const std::vector<std::pair<Die*, qint64>>& ValidatorResult::validDice() const +{ + return m_validDice; +} + +std::vector<std::pair<Die*, qint64>>& ValidatorResult::validDiceRef() +{ + return m_validDice; +} + +void ValidatorResult::appendValidDice(Die* die, qint64 sum) +{ + m_validDice.push_back(std::make_pair(die, sum)); +} + +void ValidatorResult::setAllTrue(bool allTrue) +{ + m_allTrue= allTrue; +} + +bool ValidatorResult::allTrue() const +{ + return m_allTrue; +} + +bool ValidatorResult::contains(Die* die) +{ + auto it= std::find_if(m_validDice.begin(), m_validDice.end(), + [die](const std::pair<Die*, qint64>& pair) { return pair.first == die; }); + + return it != m_validDice.end(); +} + +//////////////////////////////////// +/// \brief ValidatorList::ValidatorList +///*** +/// +//////////////////////////////////// + +ValidatorList::ValidatorList() {} + +ValidatorList::~ValidatorList() +{ + qDeleteAll(m_validatorList); +} +qint64 ValidatorList::hasValid(Die* b, bool recursive, bool unhighlight) const +{ + int i= 0; + qint64 sum= 0; + bool highLight= false; + for(auto& validator : m_validatorList) + { + qint64 val= validator->hasValid(b, recursive, unhighlight); + if(i == 0) + { + sum= val; + if(b->isHighlighted()) + { + highLight= b->isHighlighted(); + } + } + else + { + switch(m_operators.at(i - 1)) + { + case Dice::LogicOperation::OR: + sum|= val; + + if(highLight) + { + b->setHighlighted(highLight); + } + break; + case Dice::LogicOperation::EXCLUSIVE_OR: + sum^= val; /// @todo may required to be done by hand + break; + case Dice::LogicOperation::AND: + sum&= val; + break; + default: + break; + } + } + ++i; + } + + return sum; +} + +QString ValidatorList::toString() +{ + QString str= ""; + /*switch (m_operator) + { + case Equal: + str.append("="); + break; + case GreaterThan: + str.append(">"); + break; + case LesserThan: + str.append("<"); + break; + case GreaterOrEqual: + str.append(">="); + break; + case LesserOrEqual: + str.append("<="); + break; + } + return QString("[%1%2]").arg(str).arg(m_value);*/ + return str; +} +namespace +{ +Dice::CONDITION_STATE testAND(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == Dice::CONDITION_STATE::UNREACHABLE || current == Dice::CONDITION_STATE::UNREACHABLE) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(before == Dice::CONDITION_STATE::ALWAYSTRUE && current == Dice::CONDITION_STATE::ALWAYSTRUE) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testOR(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == Dice::CONDITION_STATE::UNREACHABLE && current == Dice::CONDITION_STATE::UNREACHABLE) + return Dice::CONDITION_STATE::UNREACHABLE; + else if(before == Dice::CONDITION_STATE::ALWAYSTRUE || current == Dice::CONDITION_STATE::ALWAYSTRUE) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} + +Dice::CONDITION_STATE testXOR(Dice::CONDITION_STATE before, Dice::CONDITION_STATE current) +{ + if(before == current + && (before == Dice::CONDITION_STATE::UNREACHABLE || before == Dice::CONDITION_STATE::ALWAYSTRUE)) + return Dice::CONDITION_STATE::UNREACHABLE; + else if((before != current) + && (before == Dice::CONDITION_STATE::ALWAYSTRUE || before == Dice::CONDITION_STATE::UNREACHABLE) + && (before != Dice::CONDITION_STATE::REACHABLE || current != Dice::CONDITION_STATE::REACHABLE)) + return Dice::CONDITION_STATE::ALWAYSTRUE; + else + return Dice::CONDITION_STATE::REACHABLE; +} +} // namespace + +Dice::CONDITION_STATE ValidatorList::isValidRangeSize(const std::pair<qint64, qint64>& range) const +{ + std::vector<Dice::CONDITION_STATE> vec; + std::transform(m_validatorList.begin(), m_validatorList.end(), std::back_inserter(vec), + [range](Validator* validator) -> Dice::CONDITION_STATE + { return validator->isValidRangeSize(range); }); + + auto itError= std::find(vec.begin(), vec.end(), Dice::CONDITION_STATE::ERROR_STATE); + + if(vec.size() == 1) + return vec.front(); + + if((static_cast<int>(vec.size()) != m_operators.size() + 1) || (itError != vec.end())) + { + return Dice::CONDITION_STATE::ERROR_STATE; + } + + std::size_t i= 0; + Dice::CONDITION_STATE val= Dice::CONDITION_STATE::ERROR_STATE; + for(const auto& op : m_operators) + { + auto currentState= vec[i + 1]; + if(i == 0) + { + val= vec[i]; + } + switch(op) + { + case Dice::LogicOperation::OR: + val= testAND(val, currentState); + break; + case Dice::LogicOperation::EXCLUSIVE_OR: + val= testOR(val, currentState); + break; + case Dice::LogicOperation::AND: + val= testXOR(val, currentState); + break; + case Dice::LogicOperation::NONE: + val= Dice::CONDITION_STATE::ERROR_STATE; + break; + } + + ++i; + } + return val; +} + +void ValidatorList::setOperationList(const QVector<Dice::LogicOperation>& m) +{ + m_operators= m; +} + +void ValidatorList::setValidators(const QList<Validator*>& valids) +{ + qDeleteAll(m_validatorList); + m_validatorList= valids; +} + +void ValidatorList::validResult(Result* result, bool recursive, bool unlight, + std::function<void(Die*, qint64)> functor) const +{ + std::vector<ValidatorResult> validityData; + for(auto& validator : m_validatorList) + { + ValidatorResult validResult; + switch(validator->getConditionType()) + { + case Dice::OnScalar: + { + Die die; + auto scalar= result->getResult(Dice::RESULT_TYPE::SCALAR).toInt(); + die.insertRollValue(scalar); + if(validator->hasValid(&die, recursive, unlight)) + { + validResult.setAllTrue(true); + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + break; + + if(m_validatorList.size() > 1) + { + for(auto const& die : qAsConst(diceResult->getResultList())) + { + validResult.appendValidDice(die, die->getValue()); + } + } + else + { + validResult.appendValidDice(new Die(die), die.getValue()); + } + } + } + break; + case Dice::OnEach: + { + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + break; + for(auto const& die : qAsConst(diceResult->getResultList())) + { + auto score= validator->hasValid(die, recursive, unlight); + if(score) + { + validResult.appendValidDice(die, score); + } + } + } + break; + case Dice::OnEachValue: + { + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + break; + for(auto const& die : qAsConst(diceResult->getResultList())) + { + auto score= validator->hasValid(die, recursive, unlight); + if(score) + { + validResult.appendValidDice(die, score); + } + } + } + break; + case Dice::AllOfThem: + { + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + break; + auto diceList= diceResult->getResultList(); + auto all= std::all_of(diceList.begin(), diceList.end(), + [validator, recursive, unlight](Die* die) + { return validator->hasValid(die, recursive, unlight); }); + if(all) + { + validResult.setAllTrue(true); + for(auto die : qAsConst(diceResult->getResultList())) + { + validResult.appendValidDice(die, die->getValue()); + } + } + } + break; + case Dice::OneOfThem: + { + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + break; + auto diceList= diceResult->getResultList(); + auto any= std::any_of(diceList.begin(), diceList.end(), + [validator, recursive, unlight](Die* die) + { return validator->hasValid(die, recursive, unlight); }); + if(any) + { + validResult.setAllTrue(true); + for(auto die : qAsConst(diceResult->getResultList())) + { + validResult.appendValidDice(die, die->getValue()); + } + } + } + } + validityData.push_back(validResult); + } + if(validityData.empty()) + return; + + int i= 0; + ValidatorResult finalResult; + + for(const auto& vec : validityData) + { + auto diceList= vec.validDice(); + if(i == 0) + { + std::copy(diceList.begin(), diceList.end(), std::back_inserter(finalResult.validDiceRef())); + } + else + { + auto id= i - 1; + if(m_operators.size() <= id) + continue; + + auto op= m_operators.at(id); + switch(op) + { + case Dice::LogicOperation::OR: + std::copy(diceList.begin(), diceList.end(), std::back_inserter(finalResult.validDiceRef())); + break; + case Dice::LogicOperation::AND: + mergeResultsAsAND(vec, finalResult); + break; + case Dice::LogicOperation::EXCLUSIVE_OR: + mergeResultsAsExeclusiveOR(vec, finalResult); + break; + case Dice::LogicOperation::NONE: + break; + } + } + + ++i; + } + + if(finalResult.allTrue()) + { + DiceResult* diceResult= getDiceResult(result); + if(nullptr == diceResult) + return; + auto diceList= diceResult->getResultList(); + std::transform(diceList.begin(), diceList.end(), std::back_inserter(finalResult.validDiceRef()), + [](Die* die) { + return std::pair<Die*, qint64>({die, 0}); + }); + } + + for(auto die : finalResult.validDice()) + { + functor(die.first, die.second); + } +} + +ValidatorList* ValidatorList::getCopy() const +{ + ValidatorList* val= new ValidatorList(); + val->setOperationList(m_operators); + val->setValidators(m_validatorList); + return val; +} diff --git a/src/libparser/validatorlist.h b/src/libparser/validatorlist.h new file mode 100644 index 0000000..ad2c9d4 --- /dev/null +++ b/src/libparser/validatorlist.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * http://www.rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser 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 VALIDATORLIST_H +#define VALIDATORLIST_H + +#include <QList> +#include <QString> +#include <QVector> +#include <Qt> + +#include <diceparser/diceparserhelper.h> +#include <functional> + +class Validator; +class Die; +class Result; + +class ValidatorResult +{ + +public: + ValidatorResult(); + + const std::vector<std::pair<Die*, qint64>>& validDice() const; + std::vector<std::pair<Die*, qint64>>& validDiceRef(); + void setValidDice(const std::vector<std::pair<Die*, qint64>>& pairs); + void appendValidDice(Die* die, qint64 sum); + + void setAllTrue(bool allTrue); + bool allTrue() const; + + bool contains(Die* die); + +private: + std::vector<std::pair<Die*, qint64>> m_validDice; + bool m_allTrue= false; + + /*friend bool operator>(const ValidatorResult& a, const ValidatorResult& b) + { + if(a.m_validDice.size() > b.m_validDice.size()) + return true; + if(a.m_validDice.size() == b.m_validDice.size()) + { + if(!a.m_allTrue && b.m_allTrue) + return true; + else + return false; + } + return false; + }*/ +}; +/** + * @brief The BooleanCondition class is a Validator class checking validity from logic expression. + * It manages many operators (see : @ref LogicOperator). + */ +class ValidatorList +{ +public: + ValidatorList(); + virtual ~ValidatorList(); + + virtual qint64 hasValid(Die* b, bool recursive, bool unhighlight= false) const; + + void setOperationList(const QVector<Dice::LogicOperation>& m); + void setValidators(const QList<Validator*>& valids); + + QString toString(); + + virtual Dice::CONDITION_STATE isValidRangeSize(const std::pair<qint64, qint64>& range) const; + + virtual ValidatorList* getCopy() const; + + void validResult(Result* result, bool recursive, bool unlight, std::function<void(Die*, qint64)> functor) const; + +private: + QVector<Dice::LogicOperation> m_operators; + QList<Validator*> m_validatorList; +}; + +#endif // VALIDATORLIST_H |