File indexing completed on 2024-04-28 05:37:01

0001 /***************************************************************************
0002  * Copyright (C) 2014 by Renaud Guezennec                                   *
0003  * https://rolisteam.org/contact                      *
0004  *                                                                          *
0005  *  This file is part of DiceParser                                         *
0006  *                                                                          *
0007  * DiceParser is free software; you can redistribute it and/or modify       *
0008  * it under the terms of the GNU General Public License as published by     *
0009  * the Free Software Foundation; either version 2 of the License, or        *
0010  * (at your option) any later version.                                      *
0011  *                                                                          *
0012  * This program is distributed in the hope that it will be useful,          *
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of           *
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
0015  * GNU General Public License for more details.                             *
0016  *                                                                          *
0017  * You should have received a copy of the GNU General Public License        *
0018  * along with this program; if not, write to the                            *
0019  * Free Software Foundation, Inc.,                                          *
0020  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                 *
0021  ***************************************************************************/
0022 #include "diceparser/parsingtoolbox.h"
0023 
0024 #include <QDebug>
0025 #include <QJsonArray>
0026 #include <QJsonObject>
0027 #include <QRegularExpression>
0028 #include <QString>
0029 #include <set>
0030 
0031 #include "node/allsamenode.h"
0032 #include "node/bind.h"
0033 #include "node/countexecutenode.h"
0034 #include "node/dicerollernode.h"
0035 #include "node/executionnode.h"
0036 #include "node/explodedicenode.h"
0037 #include "node/filternode.h"
0038 #include "node/groupnode.h"
0039 #include "node/helpnode.h"
0040 #include "node/ifnode.h"
0041 #include "node/jumpbackwardnode.h"
0042 #include "node/keepdiceexecnode.h"
0043 #include "node/listaliasnode.h"
0044 #include "node/listsetrollnode.h"
0045 #include "node/mergenode.h"
0046 #include "node/numbernode.h"
0047 #include "node/occurencecountnode.h"
0048 #include "node/paintnode.h"
0049 #include "node/parenthesesnode.h"
0050 #include "node/repeaternode.h"
0051 #include "node/replacevaluenode.h"
0052 #include "node/rerolldicenode.h"
0053 #include "node/scalaroperatornode.h"
0054 #include "node/sortresult.h"
0055 #include "node/splitnode.h"
0056 #include "node/startingnode.h"
0057 #include "node/stringnode.h"
0058 #include "node/switchcasenode.h"
0059 #include "node/uniquenode.h"
0060 #include "node/valueslistnode.h"
0061 #include "node/variablenode.h"
0062 #include "operationcondition.h"
0063 #include "range.h"
0064 #include "validatorlist.h"
0065 
0066 QHash<QString, QString> ParsingToolBox::m_variableHash;
0067 
0068 ParsingToolBox::ParsingToolBox()
0069 {
0070     // m_logicOp = ;
0071     m_logicOp.insert(">=", Dice::CompareOperator::GreaterOrEqual);
0072     m_logicOp.insert("<=", Dice::CompareOperator::LesserOrEqual);
0073     m_logicOp.insert("<", Dice::CompareOperator::LesserThan);
0074     m_logicOp.insert("=", Dice::CompareOperator::Equal);
0075     m_logicOp.insert(">", Dice::CompareOperator::GreaterThan);
0076     m_logicOp.insert("!=", Dice::CompareOperator::Different);
0077 
0078     // m_logicOperation = ;
0079     m_logicOperation.insert("|", Dice::LogicOperation::OR);
0080     m_logicOperation.insert("^", Dice::LogicOperation::EXCLUSIVE_OR);
0081     m_logicOperation.insert("&", Dice::LogicOperation::AND);
0082 
0083     // m_conditionOperation = ;
0084     m_conditionOperation.insert("%", Dice::ConditionOperator::Modulo);
0085 
0086     // m_arithmeticOperation = new QHash<QString,ScalarOperatorNode::ArithmeticOperator>();
0087     m_arithmeticOperation.push_back({QStringLiteral("**"), Dice::ArithmeticOperator::POW});
0088     m_arithmeticOperation.push_back({QStringLiteral("+"), Dice::ArithmeticOperator::PLUS});
0089     m_arithmeticOperation.push_back({QStringLiteral("-"), Dice::ArithmeticOperator::MINUS});
0090     m_arithmeticOperation.push_back({QStringLiteral("*"), Dice::ArithmeticOperator::MULTIPLICATION});
0091     m_arithmeticOperation.push_back({QStringLiteral("x"), Dice::ArithmeticOperator::MULTIPLICATION});
0092     m_arithmeticOperation.push_back({QStringLiteral("|"), Dice::ArithmeticOperator::INTEGER_DIVIDE});
0093     m_arithmeticOperation.push_back({QStringLiteral("/"), Dice::ArithmeticOperator::DIVIDE});
0094     m_arithmeticOperation.push_back({QStringLiteral("รท"), Dice::ArithmeticOperator::DIVIDE});
0095 
0096     m_mapDiceOp.insert(QStringLiteral("D"), D);
0097     m_mapDiceOp.insert(QStringLiteral("L"), L);
0098 
0099     m_OptionOp.insert(QStringLiteral("k"), Keep);
0100     m_OptionOp.insert(QStringLiteral("K"), KeepAndExplode);
0101     m_OptionOp.insert(QStringLiteral("s"), Sort);
0102     m_OptionOp.insert(QStringLiteral("c"), Count);
0103     m_OptionOp.insert(QStringLiteral("r"), Reroll);
0104     m_OptionOp.insert(QStringLiteral("e"), Explode);
0105     m_OptionOp.insert(QStringLiteral("R"), RerollUntil);
0106     m_OptionOp.insert(QStringLiteral("a"), RerollAndAdd);
0107     m_OptionOp.insert(QStringLiteral("m"), Merge);
0108     m_OptionOp.insert(QStringLiteral("i"), ifOperator);
0109     m_OptionOp.insert(QStringLiteral("p"), Painter);
0110     m_OptionOp.insert(QStringLiteral("f"), Filter);
0111     m_OptionOp.insert(QStringLiteral("y"), Split);
0112     m_OptionOp.insert(QStringLiteral("u"), Unique);
0113     m_OptionOp.insert(QStringLiteral("t"), AllSameExplode);
0114     m_OptionOp.insert(QStringLiteral("g"), Group);
0115     m_OptionOp.insert(QStringLiteral("b"), Bind);
0116     m_OptionOp.insert(QStringLiteral("o"), Occurences);
0117     m_OptionOp.insert(QStringLiteral("S"), SwitchCaseOption);
0118     m_OptionOp.insert(QStringLiteral("T"), TransformOption);
0119 
0120     m_functionMap.insert({QStringLiteral("repeat"), REPEAT});
0121 
0122     m_nodeActionMap.insert(QStringLiteral("@"), JumpBackward);
0123 
0124     m_commandList.append(QStringLiteral("help"));
0125     m_commandList.append(QStringLiteral("la"));
0126 }
0127 
0128 ParsingToolBox::ParsingToolBox(const ParsingToolBox&) {}
0129 ParsingToolBox::~ParsingToolBox() {}
0130 
0131 void ParsingToolBox::clearUp()
0132 {
0133     m_errorMap.clear();
0134     m_comment= QString("");
0135 }
0136 
0137 void ParsingToolBox::cleanUpAliases()
0138 {
0139     m_aliasList.clear();
0140 }
0141 
0142 ExecutionNode* ParsingToolBox::addSort(ExecutionNode* e, bool b)
0143 {
0144     SortResultNode* nodeSort= new SortResultNode();
0145     nodeSort->setSortAscending(b);
0146     if(nullptr != e)
0147         e->setNextNode(nodeSort);
0148     return nodeSort;
0149 }
0150 
0151 void ParsingToolBox::addError(Dice::ERROR_CODE code, const QString& msg)
0152 {
0153     m_errorMap.insert(code, msg);
0154 }
0155 void ParsingToolBox::addWarning(Dice::ERROR_CODE code, const QString& msg)
0156 {
0157     m_warningMap.insert(code, msg);
0158 }
0159 bool ParsingToolBox::readDiceLogicOperator(QString& str, Dice::ConditionOperator& op)
0160 {
0161     QString longKey;
0162     auto const& keys= m_conditionOperation.keys();
0163     for(const QString& tmp : keys)
0164     {
0165         if(str.startsWith(tmp))
0166         {
0167             if(longKey.size() < tmp.size())
0168             {
0169                 longKey= tmp;
0170             }
0171         }
0172     }
0173     if(longKey.size() > 0)
0174     {
0175         str= str.remove(0, longKey.size());
0176         op= m_conditionOperation.value(longKey);
0177         return true;
0178     }
0179 
0180     return false;
0181 }
0182 
0183 bool ParsingToolBox::readArithmeticOperator(QString& str, Dice::ArithmeticOperator& op)
0184 {
0185 
0186     auto it= std::find_if(
0187         m_arithmeticOperation.begin(), m_arithmeticOperation.end(),
0188         [str](const std::pair<QString, Dice::ArithmeticOperator>& pair) { return str.startsWith(pair.first); });
0189     if(it == m_arithmeticOperation.end())
0190         return false;
0191 
0192     op= it->second;
0193     str= str.remove(0, it->first.size());
0194     return true;
0195 }
0196 
0197 bool ParsingToolBox::readLogicOperator(QString& str, Dice::CompareOperator& op)
0198 {
0199     QString longKey;
0200     auto const& keys= m_logicOp.keys();
0201     for(const QString& tmp : keys)
0202     {
0203         if(str.startsWith(tmp))
0204         {
0205             if(longKey.size() < tmp.size())
0206             {
0207                 longKey= tmp;
0208             }
0209         }
0210     }
0211     if(longKey.size() > 0)
0212     {
0213         str= str.remove(0, longKey.size());
0214         op= m_logicOp.value(longKey);
0215         return true;
0216     }
0217 
0218     return false;
0219 }
0220 QString ParsingToolBox::getComment() const
0221 {
0222     return m_comment;
0223 }
0224 
0225 void ParsingToolBox::setComment(const QString& comment)
0226 {
0227     m_comment= comment;
0228 }
0229 const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getErrorList() const
0230 {
0231     return m_errorMap;
0232 }
0233 const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getWarningList() const
0234 {
0235     return m_warningMap;
0236 }
0237 bool ParsingToolBox::readOperand(QString& str, ExecutionNode*& node)
0238 {
0239     qint64 intValue= 1;
0240     QString resultStr;
0241     if(readDynamicVariable(str, intValue))
0242     {
0243         VariableNode* variableNode= new VariableNode();
0244         variableNode->setIndex(static_cast<quint64>(intValue - 1));
0245         variableNode->setData(&m_startNodes);
0246         node= variableNode;
0247         return true;
0248     }
0249     else if(readNumber(str, intValue))
0250     {
0251         NumberNode* numberNode= new NumberNode();
0252         numberNode->setNumber(intValue);
0253         node= numberNode;
0254         return true;
0255     }
0256     else if(readString(str, resultStr))
0257     {
0258         StringNode* strNode= new StringNode();
0259         strNode->setString(resultStr);
0260         node= strNode;
0261         return true;
0262     }
0263     return false;
0264 }
0265 
0266 Validator* ParsingToolBox::readValidator(QString& str, bool hasSquare)
0267 {
0268     Validator* returnVal= nullptr;
0269     auto opCompare= readConditionType(str);
0270     Dice::CompareOperator myLogicOp{Dice::CompareOperator::Equal};
0271     readLogicOperator(str, myLogicOp);
0272 
0273     Dice::ConditionOperator condiOp{Dice::ConditionOperator::Modulo};
0274     bool hasDiceLogicOperator= readDiceLogicOperator(str, condiOp);
0275     ExecutionNode* operandNode= nullptr;
0276     if(hasDiceLogicOperator)
0277     {
0278         if(readOperand(str, operandNode))
0279         {
0280             OperationCondition* condition= new OperationCondition();
0281             condition->setConditionType(opCompare);
0282             condition->setValueNode(operandNode);
0283             Validator* valid= readValidator(str, hasSquare);
0284             BooleanCondition* boolC= dynamic_cast<BooleanCondition*>(valid);
0285             if(nullptr != boolC)
0286             {
0287                 condition->setBoolean(boolC);
0288                 returnVal= condition;
0289             }
0290             else
0291             {
0292                 delete condition;
0293             }
0294         }
0295     }
0296     else if(readOperand(str, operandNode))
0297     {
0298         bool isRange= false;
0299         if(str.startsWith("..") && hasSquare)
0300         {
0301             str= str.remove(0, 2);
0302             qint64 end= 0;
0303             if(readNumber(str, end))
0304             {
0305                 str= str.remove(0, 1);
0306                 qint64 start= operandNode->getScalarResult();
0307                 Range* range= new Range();
0308                 range->setConditionType(opCompare);
0309                 range->setValue(start, end);
0310                 returnVal= range;
0311                 isRange= true;
0312             }
0313         }
0314 
0315         if(!isRange)
0316         {
0317             BooleanCondition* condition= new BooleanCondition();
0318             condition->setConditionType(opCompare);
0319             condition->setValueNode(operandNode);
0320             condition->setOperator(myLogicOp);
0321             returnVal= condition;
0322         }
0323     }
0324     return returnVal;
0325 }
0326 
0327 Dice::ConditionType ParsingToolBox::readConditionType(QString& str)
0328 {
0329     Dice::ConditionType type= Dice::OnEach;
0330     if(str.startsWith('.'))
0331     {
0332         str= str.remove(0, 1);
0333         type= Dice::OneOfThem;
0334     }
0335     else if(str.startsWith('?'))
0336     {
0337         str= str.remove(0, 1);
0338         type= Dice::OnEachValue;
0339     }
0340     else if(str.startsWith('*'))
0341     {
0342         str= str.remove(0, 1);
0343         type= Dice::AllOfThem;
0344     }
0345     else if(str.startsWith(':'))
0346     {
0347         str= str.remove(0, 1);
0348         type= Dice::OnScalar;
0349     }
0350     return type;
0351 }
0352 bool ParsingToolBox::hasError() const
0353 {
0354     return !m_errorMap.isEmpty();
0355 }
0356 ValidatorList* ParsingToolBox::readValidatorList(QString& str)
0357 {
0358     bool expectSquareBrasket= false;
0359     if((str.startsWith("[")))
0360     {
0361         str= str.remove(0, 1);
0362         expectSquareBrasket= true;
0363     }
0364     Validator* tmp= readValidator(str, expectSquareBrasket);
0365     Dice::LogicOperation opLogic;
0366 
0367     QVector<Dice::LogicOperation> operators;
0368     QList<Validator*> validatorList;
0369 
0370     while(nullptr != tmp)
0371     {
0372         bool hasOperator= readLogicOperation(str, opLogic);
0373         if(hasOperator)
0374         {
0375             operators.append(opLogic);
0376             validatorList.append(tmp);
0377             tmp= readValidator(str, expectSquareBrasket);
0378         }
0379         else
0380         {
0381             if((expectSquareBrasket) && (str.startsWith("]")))
0382             {
0383                 str= str.remove(0, 1);
0384                 // isOk=true;
0385             }
0386             validatorList.append(tmp);
0387             tmp= nullptr;
0388         }
0389     }
0390     if(!validatorList.isEmpty())
0391     {
0392         ValidatorList* validator= new ValidatorList();
0393         validator->setOperationList(operators);
0394         validator->setValidators(validatorList);
0395         return validator;
0396     }
0397     else
0398     {
0399         return nullptr;
0400     }
0401 }
0402 bool ParsingToolBox::readLogicOperation(QString& str, Dice::LogicOperation& op)
0403 {
0404     QString longKey;
0405     auto const& keys= m_logicOperation.keys();
0406     for(auto& tmp : keys)
0407     {
0408         if(str.startsWith(tmp))
0409         {
0410             if(longKey.size() < tmp.size())
0411             {
0412                 longKey= tmp;
0413             }
0414         }
0415     }
0416     if(longKey.size() > 0)
0417     {
0418         str= str.remove(0, longKey.size());
0419         op= m_logicOperation.value(longKey);
0420         return true;
0421     }
0422 
0423     return false;
0424 }
0425 
0426 bool ParsingToolBox::readNumber(QString& str, qint64& myNumber)
0427 {
0428     if(str.isEmpty())
0429         return false;
0430 
0431     QString number;
0432     int i= 0;
0433     while(i < str.length() && ((str[i].isNumber()) || ((i == 0) && (str[i] == '-'))))
0434     {
0435         number+= str[i];
0436         ++i;
0437     }
0438 
0439     if(number.isEmpty())
0440     {
0441         QString reason;
0442         return readVariable(str, myNumber, reason);
0443     }
0444 
0445     bool ok;
0446     myNumber= number.toLongLong(&ok);
0447     if(ok)
0448     {
0449         str= str.remove(0, number.size());
0450         return true;
0451     }
0452 
0453     return false;
0454 }
0455 bool ParsingToolBox::readDynamicVariable(QString& str, qint64& index)
0456 {
0457     if(str.isEmpty())
0458         return false;
0459     if(str.startsWith('$'))
0460     {
0461         QString number;
0462         int i= 1;
0463         while(i < str.length() && (str[i].isNumber()))
0464         {
0465             number+= str[i];
0466             ++i;
0467         }
0468 
0469         bool ok;
0470         index= number.toLongLong(&ok);
0471         if(ok)
0472         {
0473             str= str.remove(0, number.size() + 1);
0474             return true;
0475         }
0476     }
0477     return false;
0478 }
0479 
0480 ExecutionNode* ParsingToolBox::getLeafNode(ExecutionNode* start)
0481 {
0482     if(nullptr == start)
0483         return nullptr;
0484 
0485     ExecutionNode* next= start;
0486     while(nullptr != next->getNextNode())
0487     {
0488         next= next->getNextNode();
0489     }
0490     return next;
0491 }
0492 
0493 const std::vector<ExecutionNode*>& ParsingToolBox::getStartNodes()
0494 {
0495     return m_startNodes;
0496 }
0497 
0498 QStringList ParsingToolBox::allFirstResultAsString(bool& hasAlias) const
0499 {
0500     // QStringList allResult;
0501     QStringList stringListResult;
0502     for(auto node : m_startNodes)
0503     {
0504         QVariant var;
0505         auto stringPair= hasResultOfType(Dice::RESULT_TYPE::STRING, node);
0506         auto scalarPair= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node);
0507         if(stringPair.first)
0508         {
0509             stringListResult << stringPair.second.toString();
0510             hasAlias= true;
0511         }
0512         else if(scalarPair.first)
0513         {
0514             stringListResult << number(scalarPair.second.toReal());
0515             hasAlias= true;
0516         }
0517     }
0518     return stringListResult;
0519 }
0520 std::pair<bool, QVariant> ParsingToolBox::hasResultOfType(Dice::RESULT_TYPE type, ExecutionNode* node,
0521                                                           bool notthelast) const
0522 {
0523     bool hasValidResult= false;
0524     QVariant var;
0525     ExecutionNode* next= ParsingToolBox::getLeafNode(node);
0526     Result* result= next->getResult();
0527     while((result != nullptr) && (!hasValidResult))
0528     {
0529         bool lastResult= false;
0530         if(notthelast)
0531             lastResult= (nullptr == result->getPrevious());
0532 
0533         if(result->hasResultOfType(type) && !lastResult)
0534         {
0535             hasValidResult= true;
0536             var= result->getResult(type);
0537         }
0538         result= result->getPrevious();
0539     }
0540     return {hasValidResult, var};
0541 }
0542 
0543 QList<qreal> ParsingToolBox::scalarResultsFromEachInstruction() const
0544 {
0545     QList<qreal> resultValues;
0546     std::set<QString> alreadyVisitedNode;
0547     for(auto node : m_startNodes)
0548     {
0549         ExecutionNode* next= ParsingToolBox::getLeafNode(node);
0550         Result* result= next->getResult();
0551         bool scalarDone= false;
0552         while((result != nullptr) && (!scalarDone))
0553         {
0554             if(result->hasResultOfType(Dice::RESULT_TYPE::SCALAR))
0555             {
0556                 if(alreadyVisitedNode.find(result->getId()) == alreadyVisitedNode.end())
0557                 {
0558                     resultValues << result->getResult(Dice::RESULT_TYPE::SCALAR).toReal();
0559                     alreadyVisitedNode.insert(result->getId());
0560                 }
0561                 scalarDone= true;
0562             }
0563             result= result->getPrevious();
0564         }
0565     }
0566     return resultValues;
0567 }
0568 
0569 QList<qreal> ParsingToolBox::sumOfDiceResult() const
0570 {
0571     QList<qreal> resultValues;
0572     for(auto node : m_startNodes)
0573     {
0574         qreal resultValue= 0;
0575         ExecutionNode* next= ParsingToolBox::getLeafNode(node);
0576         Result* result= next->getResult();
0577         bool found= false;
0578         while((nullptr != result) && (!found))
0579         {
0580             if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST))
0581             {
0582                 DiceResult* myDiceResult= dynamic_cast<DiceResult*>(result);
0583                 if(nullptr != myDiceResult)
0584                 {
0585                     for(auto& die : myDiceResult->getResultList())
0586                     {
0587                         resultValue+= die->getValue();
0588                     }
0589                     found= true;
0590                 }
0591             }
0592             result= result->getPrevious();
0593         }
0594         resultValues << resultValue;
0595     }
0596     return resultValues;
0597 }
0598 
0599 std::pair<QString, QString> ParsingToolBox::finalScalarResult() const
0600 {
0601     QString scalarText;
0602     QString lastScalarText;
0603     auto listDie= diceResultFromEachInstruction();
0604     if(hasIntegerResultNotInFirst())
0605     {
0606         QStringList strLst;
0607         auto listScalar= scalarResultsFromEachInstruction();
0608         for(auto val : listScalar)
0609         {
0610             strLst << number(val);
0611         }
0612         scalarText= QString("%1").arg(strLst.join(','));
0613         lastScalarText= strLst.last();
0614     }
0615     else if(!listDie.isEmpty())
0616     {
0617         auto values= sumOfDiceResult();
0618         QStringList strLst;
0619         for(auto val : values)
0620         {
0621             strLst << number(val);
0622         }
0623         scalarText= QString("%1").arg(strLst.join(','));
0624     }
0625     return {scalarText, lastScalarText};
0626 }
0627 
0628 bool ParsingToolBox::hasIntegerResultNotInFirst() const
0629 {
0630     bool result= false;
0631     for(auto node : m_startNodes)
0632     {
0633         result|= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node, true).first;
0634     }
0635     return result;
0636 }
0637 
0638 bool ParsingToolBox::hasDiceResult() const
0639 {
0640     bool result= false;
0641     for(auto node : m_startNodes)
0642     {
0643         result|= hasResultOfType(Dice::RESULT_TYPE::DICE_LIST, node).first;
0644     }
0645     return result;
0646 }
0647 bool ParsingToolBox::hasStringResult() const
0648 {
0649     bool result= false;
0650     for(auto node : m_startNodes)
0651     {
0652         result|= hasResultOfType(Dice::RESULT_TYPE::STRING, node).first;
0653     }
0654     return result;
0655 }
0656 
0657 QList<ExportedDiceResult> ParsingToolBox::diceResultFromEachInstruction() const
0658 {
0659     QList<ExportedDiceResult> resultList;
0660     for(auto start : m_startNodes)
0661     {
0662         resultList.append(ParsingToolBox::finalDiceResultFromInstruction(start));
0663     }
0664     return resultList;
0665 }
0666 
0667 QStringList listOfDiceResult(const QList<ExportedDiceResult>& list, bool removeDouble= false)
0668 {
0669     QStringList listOfDiceResult;
0670     std::set<QString> alreadyAdded;
0671     for(auto map : list)
0672     {
0673         for(auto key : map.keys())
0674         {
0675             auto listOfList= map.value(key);
0676             for(auto dice : listOfList)
0677             {
0678                 QString stringVal;
0679                 for(auto val : dice)
0680                 {
0681                     if(removeDouble && (alreadyAdded.end() != alreadyAdded.find(val.uuid())))
0682                         continue;
0683 
0684                     alreadyAdded.insert(val.uuid());
0685 
0686                     qint64 total= 0;
0687                     QStringList dicelist;
0688                     for(auto score : val.result())
0689                     {
0690                         total+= score;
0691                         dicelist << QString::number(score);
0692                     }
0693                     if(val.result().size() > 1)
0694                     {
0695                         stringVal= QString("%1 [%2]").arg(total).arg(dicelist.join(','));
0696                         listOfDiceResult << stringVal;
0697                     }
0698                     else
0699                     {
0700                         listOfDiceResult << QString::number(total);
0701                     }
0702                 }
0703             }
0704         }
0705     }
0706     return listOfDiceResult;
0707 }
0708 
0709 QString ParsingToolBox::finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize,
0710                                           bool removeUnhighlighted) const
0711 {
0712     bool ok;
0713     QStringList allStringlist= allFirstResultAsString(ok);
0714     auto listFull= diceResultFromEachInstruction();
0715 
0716     QStringList resultWithPlaceHolder;
0717     std::for_each(allStringlist.begin(), allStringlist.end(), [&resultWithPlaceHolder](const QString& sub) {
0718         QRegularExpression ex("%[1-3]?|\\$[1-9]+|@[1-9]+");
0719         if(sub.contains(ex))
0720             resultWithPlaceHolder.append(sub);
0721     });
0722     auto stringResult= resultWithPlaceHolder.isEmpty() ? allStringlist.join(",") : resultWithPlaceHolder.join(",");
0723 
0724     auto pairScalar= finalScalarResult();
0725 
0726     stringResult.replace("%1", pairScalar.first);
0727     stringResult.replace("%2", listOfDiceResult(diceResultFromEachInstruction(), true).join(",").trimmed());
0728     stringResult.replace("%3", pairScalar.second);
0729     stringResult.replace("\\n", "\n");
0730 
0731     QMap<Dice::ERROR_CODE, QString> errorMap;
0732     stringResult= ParsingToolBox::replaceVariableToValue(stringResult, allStringlist, errorMap);
0733     stringResult= ParsingToolBox::replacePlaceHolderToValue(stringResult, listFull, removeUnhighlighted, colorize);
0734 
0735     return stringResult;
0736 }
0737 
0738 bool ParsingToolBox::readString(QString& str, QString& strResult)
0739 {
0740     if(str.isEmpty())
0741         return false;
0742 
0743     if(str.startsWith('"'))
0744     {
0745         str= str.remove(0, 1);
0746 
0747         int i= 0;
0748         int j= 0;
0749         bool previousEscape= false;
0750         QString result;
0751         /*&&
0752                 (((!previousEscape) && !(str[i]=='"')) || (previousEscape) && !(str[i]=='"'))
0753                     || (str[i]=='\\'))*/
0754         while(i < str.length() && (!(!previousEscape && (str[i] == '"')) || (previousEscape && str[i] != '"')))
0755         {
0756             if(str[i] == '\\')
0757             {
0758                 previousEscape= true;
0759             }
0760             else
0761             {
0762                 if(previousEscape && str[i] != '\"')
0763                 {
0764                     result+= '\\';
0765                     ++j;
0766                 }
0767                 result+= str[i];
0768                 previousEscape= false;
0769             }
0770             ++i;
0771         }
0772 
0773         if(!result.isEmpty())
0774         {
0775             str= str.remove(0, i);
0776             strResult= result;
0777             if(str.startsWith('"'))
0778             {
0779                 str= str.remove(0, 1);
0780                 return true;
0781             }
0782         }
0783     }
0784 
0785     return false;
0786 }
0787 
0788 bool ParsingToolBox::readVariable(QString& str, qint64& myNumber, QString& reasonFail)
0789 {
0790     if(str.isEmpty())
0791         return false;
0792 
0793     if(str.startsWith("${"))
0794     {
0795         str= str.remove(0, 2);
0796     }
0797     QString key;
0798     int post= str.indexOf('}');
0799     key= str.left(post);
0800 
0801     if(!m_variableHash.isEmpty())
0802     {
0803         if(m_variableHash.contains(key))
0804         {
0805             QString value= m_variableHash.value(key);
0806             bool ok;
0807             int valueInt= value.toInt(&ok);
0808             if(ok)
0809             {
0810                 myNumber= valueInt;
0811                 str= str.remove(0, post + 1);
0812                 return true;
0813             }
0814             else
0815             {
0816                 reasonFail= QStringLiteral("Variable value is %1, not a number").arg(value);
0817             }
0818         }
0819         else
0820         {
0821             reasonFail= QStringLiteral("Variable not found");
0822         }
0823     }
0824     else
0825     {
0826         reasonFail= QStringLiteral("No Variables are defined");
0827     }
0828 
0829     return false;
0830 }
0831 bool ParsingToolBox::readComma(QString& str)
0832 {
0833     if(str.startsWith(","))
0834     {
0835         str= str.remove(0, 1);
0836         return true;
0837     }
0838     else
0839         return false;
0840 }
0841 bool ParsingToolBox::readOpenParentheses(QString& str)
0842 {
0843     if(str.startsWith("("))
0844     {
0845         str= str.remove(0, 1);
0846         return true;
0847     }
0848     else
0849         return false;
0850 }
0851 
0852 bool ParsingToolBox::readCloseParentheses(QString& str)
0853 {
0854     if(str.startsWith(")"))
0855     {
0856         str= str.remove(0, 1);
0857         return true;
0858     }
0859     else
0860         return false;
0861 }
0862 
0863 int ParsingToolBox::findClosingCharacterIndexOf(QChar open, QChar closing, const QString& str, int offset)
0864 {
0865     int counter= offset;
0866     int i= 0;
0867     for(auto const& letter : str)
0868     {
0869         if(letter == open)
0870             ++counter;
0871         else if(letter == closing)
0872             --counter;
0873 
0874         if(counter == 0)
0875             return i;
0876 
0877         ++i;
0878     }
0879     return -1;
0880 }
0881 
0882 bool ParsingToolBox::readList(QString& str, QStringList& list, QList<Range>& ranges)
0883 {
0884     if(str.startsWith("["))
0885     {
0886         str= str.remove(0, 1);
0887         int pos= findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]");
0888         if(-1 != pos)
0889         {
0890             QString liststr= str.left(pos);
0891             list= liststr.split(",");
0892             str= str.remove(0, pos + 1);
0893             readProbability(list, ranges);
0894             return true;
0895         }
0896     }
0897     return false;
0898 }
0899 bool ParsingToolBox::readAscending(QString& str)
0900 {
0901     if(str.isEmpty())
0902     {
0903         return false;
0904     }
0905     else if(str.at(0) == 'l')
0906     {
0907         str= str.remove(0, 1);
0908         return true;
0909     }
0910     return false;
0911 }
0912 
0913 bool ParsingToolBox::readStopAtFirst(QString& str)
0914 {
0915     if(str.isEmpty())
0916         return false;
0917     else if(str.at(0) == '^')
0918     {
0919         str= str.remove(0, 1);
0920         return true;
0921     }
0922     return false;
0923 }
0924 
0925 Dice::CONDITION_STATE ParsingToolBox::isValidValidator(ExecutionNode* previous, ValidatorList* val)
0926 {
0927     DiceRollerNode* node= getDiceRollerNode(previous);
0928     if(nullptr == node)
0929         return Dice::CONDITION_STATE::ERROR_STATE;
0930 
0931     return val->isValidRangeSize(node->getRange());
0932 }
0933 DiceRollerNode* ParsingToolBox::getDiceRollerNode(ExecutionNode* previous)
0934 {
0935     while(nullptr != previous)
0936     {
0937         DiceRollerNode* node= dynamic_cast<DiceRollerNode*>(previous);
0938         if(nullptr != node)
0939         {
0940             return node;
0941         }
0942         previous= previous->getPreviousNode();
0943     }
0944     return nullptr;
0945 }
0946 bool ParsingToolBox::readDiceRange(QString& str, qint64& start, qint64& end)
0947 {
0948     bool expectSquareBrasket= false;
0949 
0950     if((str.startsWith("[")))
0951     {
0952         str= str.remove(0, 1);
0953         expectSquareBrasket= true;
0954     }
0955     if(readNumber(str, start))
0956     {
0957         if(str.startsWith(".."))
0958         {
0959             str= str.remove(0, 2);
0960             if(readNumber(str, end))
0961             {
0962                 if(expectSquareBrasket)
0963                 {
0964                     if(str.startsWith("]"))
0965                     {
0966                         str= str.remove(0, 1);
0967                         return true;
0968                     }
0969                 }
0970             }
0971         }
0972         else if(expectSquareBrasket)
0973         {
0974             if(str.startsWith("]"))
0975             {
0976                 str= str.remove(0, 1);
0977                 end= start;
0978                 return true;
0979             }
0980         }
0981     }
0982     return false;
0983 }
0984 ParsingToolBox::LIST_OPERATOR ParsingToolBox::readListOperator(QString& str)
0985 {
0986     QHash<QChar, ParsingToolBox::LIST_OPERATOR> hash;
0987     hash.insert('u', UNIQUE);
0988     hash.insert('n', NOCOMMA);
0989     bool findOne= false;
0990     ParsingToolBox::LIST_OPERATOR op= NONE;
0991     int i= 0;
0992     do
0993     {
0994         auto keys= hash.keys();
0995         findOne= false;
0996         for(auto const& key : qAsConst(keys))
0997         {
0998             if(str.startsWith(key))
0999             {
1000                 str= str.remove(0, 1);
1001                 op= hash[key];
1002                 findOne= true;
1003                 ++i;
1004             }
1005         }
1006     } while(findOne);
1007 
1008     return i > 1 ? UniqueAndNoComma : op;
1009 }
1010 
1011 bool ParsingToolBox::readPainterParameter(PainterNode* painter, QString& str)
1012 {
1013     if(!str.startsWith('['))
1014         return false;
1015 
1016     str= str.remove(0, 1);
1017     int pos= str.indexOf(']');
1018 
1019     if(pos == -1)
1020         return false;
1021 
1022     QString data= str.left(pos);
1023     str= str.remove(0, pos + 1);
1024     QStringList duos= data.split(',');
1025     bool result= false;
1026     for(QString& duoStr : duos)
1027     {
1028         QStringList keyValu= duoStr.split(':');
1029         if(keyValu.size() == 2)
1030         {
1031             painter->insertColorItem(keyValu[1], keyValu[0].toInt());
1032             result= true;
1033         }
1034     }
1035 
1036     return result;
1037 }
1038 
1039 QHash<QString, QString> ParsingToolBox::getVariableHash()
1040 {
1041     return m_variableHash;
1042 }
1043 
1044 void ParsingToolBox::setVariableHash(const QHash<QString, QString>& variableHash)
1045 {
1046     m_variableHash= variableHash;
1047 }
1048 
1049 void ParsingToolBox::setStartNodes(std::vector<ExecutionNode*> nodes)
1050 {
1051     m_startNodes= nodes;
1052 }
1053 
1054 void ParsingToolBox::readProbability(QStringList& str, QList<Range>& ranges)
1055 {
1056     quint64 totalDistance= 0;
1057     quint64 undefDistance= 0;
1058     int undefCount= 0;
1059     int maxValue= 0;
1060     int i= 0;
1061     int j= 0;
1062     bool hasPercentage= false;
1063     for(QString line : str)
1064     {
1065         int pos= line.indexOf('[');
1066         if(-1 != pos)
1067         {
1068             QString rangeStr= line.right(line.length() - pos);
1069             line= line.left(pos);
1070             str[j]= line;
1071             qint64 start= 0;
1072             qint64 end= 0;
1073             if(readDiceRange(rangeStr, start, end))
1074             {
1075                 Range range;
1076                 range.setValue(start, end);
1077                 ranges.append(range);
1078                 totalDistance+= static_cast<quint64>(end - start + 1);
1079                 ++i;
1080             }
1081             else // percentage
1082             {
1083                 hasPercentage= true;
1084                 Range range;
1085                 range.setStart(start);
1086                 ranges.append(range);
1087                 ++undefCount;
1088                 undefDistance+= static_cast<quint64>(start);
1089             }
1090             if((end > maxValue) || (i == 1))
1091             {
1092                 maxValue= static_cast<int>(end);
1093             }
1094         }
1095         else
1096         {
1097             Range range;
1098             range.setEmptyRange(true);
1099             ranges.append(range);
1100         }
1101         ++j;
1102     }
1103 
1104     if((hasPercentage) && (undefDistance != 0))
1105     {
1106         qreal ratio= 100.0 / static_cast<qreal>(undefDistance);
1107         qint64 realStart= 0;
1108         for(int i= 0; i < ranges.size(); ++i)
1109         {
1110             Range tmp= ranges.at(i);
1111             if(!tmp.isFullyDefined())
1112             {
1113                 int dist= static_cast<int>(tmp.getStart());
1114                 tmp.setStart(realStart + 1);
1115                 double truc= dist * ratio;
1116 
1117                 tmp.setEnd(static_cast<int>(realStart + truc));
1118                 realStart= tmp.getEnd();
1119                 ranges[i]= tmp;
1120             }
1121         }
1122     }
1123     else
1124     {
1125         int limitUp= 1;
1126         for(int i= 0; i < ranges.size(); ++i)
1127         {
1128             Range range= ranges.at(i);
1129             if(range.isEmptyRange())
1130             {
1131                 range.setStart(limitUp);
1132                 range.setEnd(limitUp);
1133                 range.setEmptyRange(false);
1134             }
1135             else
1136             {
1137                 qint64 sizeRange= range.getEnd() - range.getStart();
1138                 range.setStart(limitUp);
1139                 limitUp+= sizeRange;
1140                 range.setEnd(limitUp);
1141             }
1142             ++limitUp;
1143             ranges[i]= range;
1144         }
1145     }
1146 }
1147 bool ParsingToolBox::readComment(QString& str, QString& result, QString& comment)
1148 {
1149     QString left= str;
1150     str= str.trimmed();
1151     if(str.startsWith("#"))
1152     {
1153         comment= left;
1154         str= str.remove(0, 1);
1155         result= str.trimmed();
1156         str= "";
1157         return true;
1158     }
1159     return false;
1160 }
1161 
1162 QString ParsingToolBox::replaceVariableToValue(const QString& source, QStringList values,
1163                                                QMap<Dice::ERROR_CODE, QString>& errorMap)
1164 {
1165     QString result= source;
1166 
1167     int start= source.size() - 1;
1168     bool valid= true;
1169     do
1170     {
1171         auto ref= readVariableFromString(source, start);
1172         if(ref.resultIndex() > values.size())
1173         {
1174             auto error= QString("No valid value at index: $%1").arg(ref.resultIndex());
1175             errorMap.insert(Dice::ERROR_CODE::INVALID_INDEX, error);
1176             return error;
1177         }
1178 
1179         valid= ref.isValid();
1180         if(!valid)
1181             continue;
1182 
1183         result.remove(ref.position(), ref.length());
1184         auto val= values[ref.resultIndex() - 1];
1185 
1186         if(ref.subIndex() >= 0)
1187         {
1188             auto valSplit= val.split(",");
1189             if(ref.subIndex() < valSplit.size())
1190                 val= valSplit[ref.subIndex()];
1191         }
1192 
1193         if(ref.digitNumber() != 0)
1194         {
1195             auto realVal= QString("%1").arg(val, ref.digitNumber(), QChar('0'));
1196             result.insert(ref.position(), realVal);
1197         }
1198         else
1199         {
1200             result.insert(ref.position(), val);
1201         }
1202     } while(valid);
1203 
1204     return result;
1205 }
1206 
1207 QString ParsingToolBox::replacePlaceHolderFromJson(const QString& source, const QJsonObject& obj)
1208 {
1209     QStringList resultList;
1210     auto instructions= obj["instructions"].toArray();
1211     std::vector<std::vector<std::pair<int, QList<QStringList>>>> instructionResult;
1212     for(auto inst : qAsConst(instructions))
1213     {
1214         std::vector<std::pair<int, QList<QStringList>>> map;
1215         auto obj= inst.toObject();
1216         auto vals= obj["diceval"].toArray();
1217         int lastFace= -1;
1218         for(auto const& valRef : qAsConst(vals))
1219         {
1220             auto diceObj= valRef.toObject();
1221             auto face= diceObj["face"].toInt();
1222             auto it= std::find_if(std::begin(map), std::end(map),
1223                                   [face](const std::pair<int, QList<QStringList>>& val) { return val.first == face; });
1224 
1225             auto realVal= diceObj["string"].toString();
1226             if(lastFace == -1 || lastFace != face)
1227             {
1228                 QList<QStringList> listOfList;
1229                 listOfList << (QStringList() << realVal);
1230                 map.push_back({face, listOfList});
1231             }
1232             else if(lastFace == face)
1233             {
1234                 auto& valList= it->second.last();
1235                 valList.append(realVal);
1236             }
1237             lastFace= face;
1238         }
1239         instructionResult.push_back(map);
1240     }
1241     std::transform(std::begin(instructionResult), std::end(instructionResult), std::back_inserter(resultList),
1242                    [](const std::vector<std::pair<int, QList<QStringList>>>& map) {
1243                        QStringList valuesStr;
1244                        auto multiKey= (map.size() > 1);
1245                        for(auto item : map)
1246                        {
1247                            auto face= item.first;
1248                            auto valueList= item.second;
1249                            QStringList strs;
1250                            for(auto list : valueList)
1251                            {
1252                                strs << list.join(",");
1253                            }
1254                            if(!multiKey)
1255                                valuesStr << strs.join(",");
1256                            else
1257                                valuesStr << QString("d%1:(%2)").arg(face).arg(strs.join(","));
1258                        }
1259                        return valuesStr.join(" - ");
1260                    });
1261 
1262     QString result= source;
1263     int start= source.size() - 1;
1264     bool valid= true;
1265     do
1266     {
1267         auto ref= readPlaceHolderFromString(source, start);
1268         if(ref.isValid())
1269         {
1270             result.remove(ref.position(), ref.length());
1271             auto val= resultList[ref.resultIndex() - 1];
1272             result.insert(ref.position(), val);
1273         }
1274         else
1275         {
1276             valid= false;
1277         }
1278     } while(valid);
1279 
1280     return result;
1281 }
1282 
1283 QString ParsingToolBox::replacePlaceHolderToValue(const QString& source, const QList<ExportedDiceResult>& list,
1284                                                   bool removeUnhighlighted,
1285                                                   std::function<QString(const QString&, const QString&, bool)> colorize)
1286 {
1287     QStringList resultList;
1288     std::transform(
1289         std::begin(list), std::end(list), std::back_inserter(resultList),
1290         [removeUnhighlighted, colorize](const ExportedDiceResult& dice) {
1291             QStringList valuesStr;
1292             if(dice.size() == 1)
1293             {
1294                 auto values= dice.values();
1295                 std::transform(
1296                     std::begin(values), std::end(values), std::back_inserter(valuesStr),
1297                     [removeUnhighlighted, colorize](const QList<ListDiceResult>& dice) {
1298                         QStringList textList;
1299                         std::transform(
1300                             std::begin(dice), std::end(dice), std::back_inserter(textList),
1301                             [removeUnhighlighted, colorize](const ListDiceResult& dice) {
1302                                 QStringList list;
1303                                 ListDiceResult values= dice;
1304                                 if(removeUnhighlighted)
1305                                 {
1306                                     values.clear();
1307                                     std::copy_if(std::begin(dice), std::end(dice), std::back_inserter(values),
1308                                                  [](const HighLightDice& hl) { return hl.isHighlighted(); });
1309                                 }
1310 
1311                                 std::transform(std::begin(values), std::end(values), std::back_inserter(list),
1312                                                [colorize](const HighLightDice& hl) {
1313                                                    return colorize(hl.getResultString(), {}, hl.isHighlighted());
1314                                                });
1315                                 return list.join(",");
1316                             });
1317                         textList.removeAll(QString());
1318                         return textList.join(",");
1319                     });
1320             }
1321             else if(dice.size() > 1)
1322             {
1323                 for(auto key : dice.keys())
1324                 {
1325                     auto list= dice.value(key);
1326                     for(auto values : list)
1327                     {
1328                         QStringList textVals;
1329                         std::transform(std::begin(values), std::end(values), std::back_inserter(textVals),
1330                                        [](const HighLightDice& dice) { return dice.getResultString(); });
1331                         valuesStr.append(QString("d%1 [%2]").arg(key).arg(textVals.join(",")));
1332                     }
1333                 }
1334             }
1335             return valuesStr.join(",");
1336         });
1337 
1338     QString result= source;
1339     int start= source.size() - 1;
1340     bool valid= true;
1341     do
1342     {
1343         auto ref= readPlaceHolderFromString(source, start);
1344         if(ref.isValid())
1345         {
1346             result.remove(ref.position(), ref.length());
1347             auto val= resultList[ref.resultIndex() - 1];
1348             result.insert(ref.position(), val);
1349         }
1350         else
1351         {
1352             valid= false;
1353         }
1354     } while(valid);
1355 
1356     return result;
1357 }
1358 void ParsingToolBox::readSubtitutionParameters(SubtituteInfo& info, QString& rest)
1359 {
1360     auto sizeS= rest.size();
1361     if(rest.startsWith("{"))
1362     {
1363         rest= rest.remove(0, 1);
1364         qint64 number;
1365         if(readNumber(rest, number))
1366         {
1367             if(rest.startsWith("}"))
1368             {
1369                 rest= rest.remove(0, 1);
1370                 info.setDigitNumber(static_cast<int>(number));
1371             }
1372         }
1373     }
1374     if(rest.startsWith("["))
1375     {
1376         rest= rest.remove(0, 1);
1377         qint64 number;
1378         if(readNumber(rest, number))
1379         {
1380             if(rest.startsWith("]"))
1381             {
1382                 rest= rest.remove(0, 1);
1383                 info.setSubIndex(static_cast<int>(number));
1384             }
1385         }
1386     }
1387     info.setLength(info.length() + sizeS - rest.size());
1388 }
1389 
1390 bool ParsingToolBox::readReaperArguments(RepeaterNode* node, QString& source)
1391 {
1392     if(!readOpenParentheses(source))
1393         return false;
1394 
1395     auto instructions= readInstructionList(source, false);
1396     if(instructions.empty())
1397         return false;
1398 
1399     readComma(source);
1400     ExecutionNode* tmp;
1401     if(readOperand(source, tmp))
1402     {
1403         if(source.startsWith("+"))
1404         {
1405             node->setSumAll(true);
1406             source= source.remove(0, 1);
1407         }
1408         if(readCloseParentheses(source))
1409         {
1410             node->setCommand(instructions);
1411             node->setTimeNode(tmp);
1412             return true;
1413         }
1414     }
1415 
1416     return false;
1417 }
1418 bool ParsingToolBox::readExpression(QString& str, ExecutionNode*& node)
1419 {
1420     ExecutionNode* operandNode= nullptr;
1421     if(readOpenParentheses(str))
1422     {
1423         ExecutionNode* internalNode= nullptr;
1424         if(readExpression(str, internalNode))
1425         {
1426             ParenthesesNode* parentheseNode= new ParenthesesNode();
1427             parentheseNode->setInternelNode(internalNode);
1428             node= parentheseNode;
1429             if(readCloseParentheses(str))
1430             {
1431                 ExecutionNode* diceNode= nullptr;
1432                 ExecutionNode* operatorNode= nullptr;
1433                 if(readDice(str, diceNode))
1434                 {
1435                     parentheseNode->setNextNode(diceNode);
1436                 }
1437                 else if(readExpression(str, operatorNode))
1438                 {
1439                     parentheseNode->setNextNode(operatorNode);
1440                 }
1441                 return true;
1442             }
1443             else
1444             {
1445                 m_warningMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE,
1446                                     QObject::tr("Expected closing parenthesis - can't validate the inside."));
1447             }
1448         }
1449     }
1450     else if(readFunction(str, operandNode))
1451     {
1452         node= operandNode;
1453         return true;
1454     }
1455     else if(readOptionFromNull(str, operandNode))
1456     {
1457         node= operandNode;
1458         return true;
1459     }
1460     else if(readOperatorFromNull(str, operandNode))
1461     {
1462         node= operandNode;
1463         return true;
1464     }
1465     else if(readOperand(str, operandNode))
1466     {
1467         ExecutionNode* diceNode= nullptr;
1468         if(readDice(str, diceNode))
1469         {
1470             operandNode->setNextNode(diceNode);
1471         }
1472         node= operandNode;
1473 
1474         operandNode= ParsingToolBox::getLeafNode(operandNode);
1475         // ExecutionNode* operatorNode=nullptr;
1476         while(readOperator(str, operandNode))
1477         {
1478             // operandNode->setNextNode(operatorNode);
1479             operandNode= ParsingToolBox::getLeafNode(operandNode);
1480         }
1481         return true;
1482     }
1483     else if(readCommand(str, operandNode))
1484     {
1485         node= operandNode;
1486         return true;
1487     }
1488     else if(readNode(str, operandNode))
1489     {
1490         node= operandNode;
1491         return true;
1492     }
1493     else if(readValuesList(str, operandNode))
1494     {
1495         node= operandNode;
1496         return true;
1497     }
1498     else
1499     {
1500         ExecutionNode* diceNode= nullptr;
1501         if(readDice(str, diceNode))
1502         {
1503             NumberNode* numberNode= new NumberNode();
1504             numberNode->setNumber(1);
1505             numberNode->setNextNode(diceNode);
1506             node= numberNode;
1507             return true;
1508         }
1509         else
1510         {
1511             return false;
1512         }
1513     }
1514     return false;
1515 }
1516 
1517 bool ParsingToolBox::readValuesList(QString& str, ExecutionNode*& node)
1518 {
1519     if(!str.startsWith("["))
1520         return false;
1521 
1522     str= str.remove(0, 1);
1523     int pos= ParsingToolBox::findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]");
1524     if(-1 == pos)
1525         return false;
1526 
1527     QString liststr= str.left(pos);
1528     auto list= liststr.split(",");
1529     str= str.remove(0, pos + 1);
1530     auto values= new ValuesListNode();
1531     for(auto var : qAsConst(list))
1532     {
1533         qint64 number= 1;
1534         var= var.trimmed();
1535         if(ParsingToolBox::readDynamicVariable(var, number))
1536         {
1537             VariableNode* variableNode= new VariableNode();
1538             variableNode->setIndex(static_cast<quint64>(number - 1));
1539             variableNode->setData(&m_startNodes);
1540             values->insertValue(variableNode);
1541         }
1542         else if(ParsingToolBox::readNumber(var, number))
1543         {
1544             NumberNode* numberNode= new NumberNode();
1545             numberNode->setNumber(number);
1546             values->insertValue(numberNode);
1547         }
1548     }
1549     node= values;
1550     return true;
1551 }
1552 bool ParsingToolBox::readOptionFromNull(QString& str, ExecutionNode*& node)
1553 {
1554     StartingNode nodePrevious;
1555     if(readOption(str, &nodePrevious))
1556     {
1557         auto nodeNext= nodePrevious.getNextNode();
1558         nodePrevious.setNextNode(nullptr);
1559         node= nodeNext;
1560         return true;
1561     }
1562     return false;
1563 }
1564 
1565 void ParsingToolBox::setHelpPath(const QString& path)
1566 {
1567     m_helpPath= path;
1568 }
1569 
1570 bool ParsingToolBox::readOperatorFromNull(QString& str, ExecutionNode*& node)
1571 {
1572     StartingNode nodePrevious;
1573     if(readOperator(str, &nodePrevious))
1574     {
1575         auto nodeNext= nodePrevious.getNextNode();
1576         nodePrevious.setNextNode(nullptr);
1577         node= nodeNext;
1578         return true;
1579     }
1580     return false;
1581 }
1582 
1583 bool ParsingToolBox::readOption(QString& str, ExecutionNode* previous) //,
1584 {
1585     if(str.isEmpty())
1586     {
1587         return false;
1588     }
1589 
1590     bool found= false;
1591     auto keys= m_OptionOp.keys();
1592     for(int i= 0; ((i < keys.size()) && (!found)); ++i)
1593     {
1594         QString key= keys.at(i);
1595 
1596         if(str.startsWith(key))
1597         {
1598             str= str.remove(0, key.size());
1599             auto operatorName= m_OptionOp.value(key);
1600             switch(operatorName)
1601             {
1602             case Keep:
1603             {
1604                 ExecutionNode* value= nullptr;
1605                 bool ascending= readAscending(str);
1606 
1607                 if(readOperand(str, value))
1608                 {
1609                     auto node= addSort(previous, ascending);
1610                     KeepDiceExecNode* nodeK= new KeepDiceExecNode();
1611                     nodeK->setDiceKeepNumber(value);
1612                     node->setNextNode(nodeK);
1613                     found= true;
1614                 }
1615             }
1616             break;
1617             case KeepAndExplode:
1618             {
1619                 bool ascending= readAscending(str);
1620                 ExecutionNode* value= nullptr;
1621                 if(readOperand(str, value))
1622                 {
1623                     DiceRollerNode* nodeTmp= dynamic_cast<DiceRollerNode*>(previous);
1624                     if(nullptr != nodeTmp)
1625                     {
1626                         previous= addExplodeDiceNode(static_cast<qint64>(nodeTmp->getFaces()), previous);
1627                     }
1628 
1629                     auto node= addSort(previous, ascending);
1630 
1631                     KeepDiceExecNode* nodeK= new KeepDiceExecNode();
1632                     nodeK->setDiceKeepNumber(value);
1633 
1634                     node->setNextNode(nodeK);
1635                     node= nodeK;
1636                     found= true;
1637                 }
1638             }
1639             break;
1640             case Filter:
1641             {
1642                 auto validatorList= readValidatorList(str);
1643                 if(nullptr != validatorList)
1644                 {
1645                     auto validity= isValidValidator(previous, validatorList);
1646 
1647                     FilterNode* filterNode= new FilterNode();
1648                     filterNode->setValidatorList(validatorList);
1649 
1650                     previous->setNextNode(filterNode);
1651                     found= true;
1652                 }
1653             }
1654             break;
1655             case Sort:
1656             {
1657                 bool ascending= readAscending(str);
1658                 addSort(previous, ascending);
1659                 /*if(!hasDice)
1660                     {
1661                         m_errorMap.insert(ExecutionNode::BAD_SYNTAXE,QObject::tr("Sort Operator does not support default
1662                    dice. You should add dice command before the s"));
1663                     }*/
1664                 found= true;
1665             }
1666             break;
1667             case Count:
1668             {
1669                 auto validatorList= readValidatorList(str);
1670                 if(nullptr != validatorList)
1671                 {
1672                     auto validity= isValidValidator(previous, validatorList);
1673 
1674                     CountExecuteNode* countNode= new CountExecuteNode();
1675                     countNode->setValidatorList(validatorList);
1676 
1677                     previous->setNextNode(countNode);
1678                     found= true;
1679                 }
1680                 else
1681                 {
1682                     m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE,
1683                                       QObject::tr("Validator is missing after the c operator. Please, change it"));
1684                 }
1685             }
1686             break;
1687             case Reroll:
1688             case RerollUntil:
1689             case RerollAndAdd:
1690                 // Todo: I think that Exploding and Rerolling could share the same code
1691                 {
1692                     auto validatorList= readValidatorList(str);
1693                     QString symbol= m_OptionOp.key(operatorName);
1694                     if(nullptr != validatorList)
1695                     {
1696                         switch(isValidValidator(previous, validatorList))
1697                         {
1698                         case Dice::CONDITION_STATE::ALWAYSTRUE:
1699                             if(operatorName == RerollAndAdd)
1700                             {
1701                                 m_errorMap.insert(
1702                                     Dice::ERROR_CODE::ENDLESS_LOOP_ERROR,
1703                                     QObject::tr("Validator is always true for the %1 operator. Please, change it")
1704                                         .arg(symbol));
1705                             }
1706                             break;
1707                         case Dice::CONDITION_STATE::UNREACHABLE:
1708                             if(operatorName == RerollUntil)
1709                             {
1710                                 m_errorMap.insert(
1711                                     Dice::ERROR_CODE::ENDLESS_LOOP_ERROR,
1712                                     QObject::tr("Condition can't be reached, causing endless loop. Please, "
1713                                                 "change the %1 option condition")
1714                                         .arg(symbol));
1715                             }
1716                             break;
1717                         case Dice::CONDITION_STATE::ERROR_STATE:
1718                         default:
1719                             break;
1720                         }
1721 
1722                         auto reroll= (operatorName == RerollAndAdd || operatorName == Reroll);
1723                         auto addingMode= (operatorName == RerollAndAdd);
1724                         RerollDiceNode* rerollNode= new RerollDiceNode(reroll, addingMode);
1725                         ExecutionNode* nodeParam= nullptr;
1726                         if(readParameterNode(str, nodeParam))
1727                         {
1728                             rerollNode->setInstruction(nodeParam);
1729                         }
1730                         rerollNode->setValidatorList(validatorList);
1731                         previous->setNextNode(rerollNode);
1732                         found= true;
1733                     }
1734                     else
1735                     {
1736                         m_errorMap.insert(
1737                             Dice::ERROR_CODE::BAD_SYNTAXE,
1738                             QObject::tr("Validator is missing after the %1 operator. Please, change it").arg(symbol));
1739                     }
1740                 }
1741                 break;
1742             case Explode:
1743             {
1744                 ExecutionNode* limit= nullptr;
1745                 auto hasLimit= readParameterNode(str, limit);
1746 
1747                 auto validatorList= readValidatorList(str);
1748                 if(nullptr != validatorList)
1749                 {
1750                     if(Dice::CONDITION_STATE::ALWAYSTRUE == isValidValidator(previous, validatorList))
1751                     {
1752                         m_errorMap.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR,
1753                                           QObject::tr("This condition %1 introduces an endless loop. Please, change it")
1754                                               .arg(validatorList->toString()));
1755                     }
1756                     ExplodeDiceNode* explodedNode= new ExplodeDiceNode();
1757 
1758                     if(hasLimit)
1759                     {
1760                         explodedNode->setLimitNode(limit);
1761                     }
1762                     explodedNode->setValidatorList(validatorList);
1763                     previous->setNextNode(explodedNode);
1764                     found= true;
1765                 }
1766                 else
1767                 {
1768                     m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE,
1769                                       QObject::tr("Validator is missing after the e operator. Please, change it"));
1770                 }
1771             }
1772             break;
1773             case Merge:
1774             {
1775                 MergeNode* mergeNode= new MergeNode();
1776                 mergeNode->setStartList(&m_startNodes);
1777                 previous->setNextNode(mergeNode);
1778                 found= true;
1779             }
1780             break;
1781             case AllSameExplode:
1782             {
1783                 AllSameNode* allSame= new AllSameNode();
1784                 previous->setNextNode(allSame);
1785                 found= true;
1786             }
1787             break;
1788             case SwitchCaseOption:
1789             {
1790                 auto scNode= new SwitchCaseNode();
1791                 found= readSwitchCaseNode(str, scNode);
1792                 previous->setNextNode(scNode);
1793             }
1794             break;
1795             case TransformOption:
1796             {
1797                 auto scNode= new ReplaceValueNode();
1798                 found= readReplaceValueNode(str, scNode);
1799                 previous->setNextNode(scNode);
1800             }
1801             break;
1802             case Bind:
1803             {
1804                 BindNode* bindNode= new BindNode();
1805                 bindNode->setStartList(&m_startNodes);
1806                 previous->setNextNode(bindNode);
1807                 found= true;
1808             }
1809             break;
1810             case Occurences:
1811             {
1812                 qint64 number= 0;
1813                 auto occNode= new OccurenceCountNode();
1814                 if(readNumber(str, number))
1815                 {
1816                     occNode->setWidth(number);
1817                     auto validatorList= readValidatorList(str);
1818                     if(validatorList)
1819                     {
1820                         occNode->setValidatorList(validatorList);
1821                     }
1822                     else if(readComma(str))
1823                     {
1824                         if(readNumber(str, number))
1825                         {
1826                             occNode->setHeight(number);
1827                         }
1828                     }
1829                 }
1830                 previous->setNextNode(occNode);
1831                 found= true;
1832             }
1833             break;
1834             case Unique:
1835             {
1836                 auto node= new UniqueNode();
1837                 previous->setNextNode(node);
1838                 found= true;
1839             }
1840             break;
1841             case Painter:
1842             {
1843                 PainterNode* painter= new PainterNode();
1844                 if(!readPainterParameter(painter, str))
1845                 {
1846                     m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE,
1847                                       QObject::tr("Missing parameter for Painter node (p)"));
1848                     delete painter;
1849                 }
1850                 else
1851                 {
1852                     previous->setNextNode(painter);
1853                     found= true;
1854                 }
1855             }
1856             break;
1857             case ifOperator:
1858             {
1859                 IfNode* nodeif= new IfNode();
1860                 nodeif->setConditionType(readConditionType(str));
1861                 auto validatorList= readValidatorList(str);
1862                 if(nullptr != validatorList)
1863                 {
1864                     ExecutionNode* trueNode= nullptr;
1865                     ExecutionNode* falseNode= nullptr;
1866                     if(readIfInstruction(str, trueNode, falseNode))
1867                     {
1868                         nodeif->setInstructionTrue(trueNode);
1869                         nodeif->setInstructionFalse(falseNode);
1870                         nodeif->setValidatorList(validatorList);
1871                         previous->setNextNode(nodeif);
1872                         found= true;
1873                     }
1874                     else
1875                     {
1876                         delete nodeif;
1877                     }
1878                 }
1879                 else
1880                 {
1881                     delete nodeif;
1882                 }
1883                 break;
1884             }
1885             case Split:
1886             {
1887                 SplitNode* splitnode= new SplitNode();
1888                 previous->setNextNode(splitnode);
1889                 found= true;
1890             }
1891             break;
1892             case Group:
1893             {
1894                 bool stringResult= readStringResultParameter(str);
1895                 qint64 groupNumber= 0;
1896                 if(readNumber(str, groupNumber))
1897                 {
1898                     GroupNode* groupNode= new GroupNode(stringResult);
1899                     groupNode->setGroupValue(groupNumber);
1900                     previous->setNextNode(groupNode);
1901                     found= true;
1902                 }
1903             }
1904             break;
1905             }
1906         }
1907     }
1908     return found;
1909 }
1910 bool ParsingToolBox::readStringResultParameter(QString& str)
1911 {
1912     if(str.startsWith("s"))
1913     {
1914         str.remove(0, 1);
1915         return true;
1916     }
1917     return false;
1918 }
1919 bool ParsingToolBox::readIfInstruction(QString& str, ExecutionNode*& trueNode, ExecutionNode*& falseNode)
1920 {
1921     if(readBlocInstruction(str, trueNode))
1922     {
1923         if(readBlocInstruction(str, falseNode))
1924         {
1925             return true;
1926         }
1927         return true;
1928     }
1929     return false;
1930 }
1931 DiceRollerNode* ParsingToolBox::addRollDiceNode(qint64 faces, ExecutionNode* previous)
1932 {
1933     DiceRollerNode* mydiceRoller= new DiceRollerNode(faces);
1934     previous->setNextNode(mydiceRoller);
1935     return mydiceRoller;
1936 }
1937 ExplodeDiceNode* ParsingToolBox::addExplodeDiceNode(qint64 value, ExecutionNode* previous)
1938 {
1939     ExplodeDiceNode* explodeDiceNode= new ExplodeDiceNode();
1940     NumberNode* node= new NumberNode();
1941     node->setNumber(value);
1942     BooleanCondition* condition= new BooleanCondition();
1943     condition->setConditionType(Dice::OnEach);
1944     condition->setValueNode(node);
1945     condition->setOperator(Dice::CompareOperator::Equal);
1946     auto valList= new ValidatorList();
1947     valList->setValidators(QList<Validator*>() << condition);
1948     auto validity= isValidValidator(previous, valList);
1949     explodeDiceNode->setValidatorList(valList);
1950     previous->setNextNode(explodeDiceNode);
1951     return explodeDiceNode;
1952 }
1953 bool ParsingToolBox::readParameterNode(QString& str, ExecutionNode*& node)
1954 {
1955     if(!str.startsWith("("))
1956         return false;
1957 
1958     str= str.remove(0, 1);
1959     if(readExpression(str, node))
1960     {
1961         if(str.startsWith(")"))
1962         {
1963             str= str.remove(0, 1);
1964             return true;
1965         }
1966     }
1967 
1968     return false;
1969 }
1970 
1971 bool ParsingToolBox::readSwitchCaseNode(QString& str, SwitchCaseNode* node)
1972 {
1973     bool res= false;
1974     node->setStopAtFirt(ParsingToolBox::readStopAtFirst(str));
1975     bool hasInstructionBloc= false;
1976     do
1977     {
1978         auto validator= readValidatorList(str);
1979         ExecutionNode* exeNode= nullptr;
1980         hasInstructionBloc= readBlocInstruction(str, exeNode);
1981         if(hasInstructionBloc)
1982         {
1983             node->insertCase(exeNode, validator);
1984             res= true;
1985         }
1986 
1987     } while(hasInstructionBloc);
1988 
1989     return res;
1990 }
1991 
1992 bool ParsingToolBox::readReplaceValueNode(QString& str, ReplaceValueNode* node)
1993 {
1994     bool res= false;
1995     bool hasInstructionBloc= false;
1996     do
1997     {
1998         auto validator= readValidatorList(str);
1999         ExecutionNode* exeNode= nullptr;
2000         hasInstructionBloc= readBlocInstruction(str, exeNode);
2001         if(hasInstructionBloc)
2002         {
2003             node->insertCase(exeNode, validator);
2004             res= true;
2005         }
2006 
2007     } while(hasInstructionBloc);
2008 
2009     return res;
2010 }
2011 
2012 bool ParsingToolBox::readBlocInstruction(QString& str, ExecutionNode*& resultnode)
2013 {
2014     if(str.startsWith('{'))
2015     {
2016         str= str.remove(0, 1);
2017         ExecutionNode* node= nullptr;
2018         Dice::ArithmeticOperator op;
2019         ScalarOperatorNode* scalarNode= nullptr;
2020         if(readArithmeticOperator(str, op))
2021         {
2022             scalarNode= new ScalarOperatorNode();
2023             scalarNode->setArithmeticOperator(op);
2024         }
2025         if(readExpression(str, node))
2026         {
2027             if(str.startsWith('}'))
2028             {
2029                 if(nullptr == scalarNode)
2030                 {
2031                     resultnode= node;
2032                 }
2033                 else
2034                 {
2035                     resultnode= scalarNode;
2036                     scalarNode->setInternalNode(node);
2037                 }
2038                 str= str.remove(0, 1);
2039                 return true;
2040             }
2041         }
2042     }
2043     return false;
2044 }
2045 bool ParsingToolBox::readDice(QString& str, ExecutionNode*& node)
2046 {
2047     DiceOperator currentOperator;
2048 
2049     if(readDiceOperator(str, currentOperator))
2050     {
2051         if(currentOperator == D)
2052         {
2053             qint64 max;
2054             qint64 min;
2055             bool unique= (ParsingToolBox::UNIQUE == readListOperator(str)) ? true : false;
2056             Dice::ArithmeticOperator op;
2057 
2058             bool hasOp= readArithmeticOperator(str, op);
2059             if(readNumber(str, max))
2060             {
2061                 if(max < 1)
2062                 {
2063                     m_errorMap.insert(
2064                         Dice::ERROR_CODE::BAD_SYNTAXE,
2065                         QObject::tr("Dice with %1 face(s) does not exist. Please, put a value higher than 0").arg(max));
2066                     return false;
2067                 }
2068                 DiceRollerNode* drNode= new DiceRollerNode(max);
2069                 drNode->setUnique(unique);
2070                 if(hasOp)
2071                 {
2072                     drNode->setOperator(op);
2073                 }
2074                 node= drNode;
2075                 ExecutionNode* current= drNode;
2076                 while(readOption(str, current))
2077                 {
2078                     current= ParsingToolBox::getLeafNode(current);
2079                 }
2080                 return true;
2081             }
2082             else if(readDiceRange(str, min, max))
2083             {
2084                 DiceRollerNode* drNode= new DiceRollerNode(max, min);
2085                 drNode->setUnique(unique);
2086                 if(hasOp)
2087                 {
2088                     drNode->setOperator(op);
2089                 }
2090                 node= drNode;
2091                 ExecutionNode* current= drNode;
2092                 while(readOption(str, current))
2093                 {
2094                     current= ParsingToolBox::getLeafNode(current);
2095                 }
2096                 return true;
2097             }
2098         }
2099         else if(currentOperator == L)
2100         {
2101             QStringList list;
2102             QList<Range> listRange;
2103             ParsingToolBox::LIST_OPERATOR op= readListOperator(str);
2104             if(readList(str, list, listRange))
2105             {
2106                 ListSetRollNode* lsrNode= new ListSetRollNode();
2107                 lsrNode->setRangeList(listRange);
2108                 if(op == ParsingToolBox::UNIQUE || op == ParsingToolBox::UniqueAndNoComma)
2109                 {
2110                     lsrNode->setUnique(true);
2111                 }
2112                 if(op == ParsingToolBox::NOCOMMA || op == ParsingToolBox::UniqueAndNoComma)
2113                 {
2114                     lsrNode->setNoComma(true);
2115                 }
2116                 lsrNode->setListValue(list);
2117                 node= lsrNode;
2118                 return true;
2119             }
2120             else
2121             {
2122                 m_errorMap.insert(
2123                     Dice::ERROR_CODE::BAD_SYNTAXE,
2124                     QObject::tr(
2125                         "List is missing after the L operator. Please, add it (e.g : 1L[sword,spear,gun,arrow])"));
2126             }
2127         }
2128     }
2129 
2130     return false;
2131 }
2132 bool ParsingToolBox::readDiceOperator(QString& str, DiceOperator& op)
2133 {
2134     QStringList listKey= m_mapDiceOp.keys();
2135     for(const QString& key : qAsConst(listKey))
2136     {
2137         if(str.startsWith(key, Qt::CaseInsensitive))
2138         {
2139             str= str.remove(0, key.size());
2140             op= m_mapDiceOp.value(key);
2141             return true;
2142         }
2143     }
2144     return false;
2145 }
2146 QString ParsingToolBox::convertAlias(QString str)
2147 {
2148     for(auto& cmd : m_aliasList)
2149     {
2150         if(cmd->isEnable())
2151         {
2152             cmd->resolved(str);
2153         }
2154     }
2155     return str;
2156 }
2157 
2158 bool ParsingToolBox::readCommand(QString& str, ExecutionNode*& node)
2159 {
2160     if(m_commandList.contains(str))
2161     {
2162         if(str == QLatin1String("help"))
2163         {
2164             str= str.remove(0, QLatin1String("help").size());
2165             HelpNode* help= new HelpNode();
2166             if(!m_helpPath.isEmpty())
2167             {
2168                 help->setHelpPath(m_helpPath);
2169             }
2170             node= help;
2171         }
2172         else if(str == QLatin1String("la"))
2173         {
2174             str= str.remove(0, QLatin1String("la").size());
2175             node= new ListAliasNode(m_aliasList);
2176         }
2177         return true;
2178     }
2179     return false;
2180 }
2181 
2182 bool ParsingToolBox::readDiceExpression(QString& str, ExecutionNode*& node)
2183 {
2184     bool returnVal= false;
2185 
2186     ExecutionNode* next= nullptr;
2187     if(readDice(str, next))
2188     {
2189         ExecutionNode* latest= next;
2190         while(readOption(str, latest))
2191         {
2192             while(nullptr != latest->getNextNode())
2193             {
2194                 latest= latest->getNextNode();
2195             }
2196         }
2197 
2198         node= next;
2199         returnVal= true;
2200     }
2201     else
2202     {
2203         returnVal= false;
2204     }
2205     return returnVal;
2206 }
2207 
2208 bool ParsingToolBox::readOperator(QString& str, ExecutionNode* previous)
2209 {
2210     bool result= false;
2211     if(str.isEmpty() || nullptr == previous)
2212     {
2213         return result;
2214     }
2215 
2216     Dice::ArithmeticOperator op;
2217     if(readArithmeticOperator(str, op))
2218     {
2219         ScalarOperatorNode* node= new ScalarOperatorNode();
2220         node->setArithmeticOperator(op);
2221         ExecutionNode* nodeExec= nullptr;
2222         if(readExpression(str, nodeExec))
2223         {
2224             node->setInternalNode(nodeExec);
2225             if(nullptr == nodeExec)
2226             {
2227                 delete node;
2228                 return result;
2229             }
2230             ExecutionNode* nodeExecOrChild= nodeExec;
2231             ExecutionNode* parent= nullptr;
2232 
2233             while((nullptr != nodeExecOrChild) && (node->getPriority() < nodeExecOrChild->getPriority()))
2234             {
2235                 parent= nodeExecOrChild;
2236                 nodeExecOrChild= nodeExecOrChild->getNextNode();
2237             }
2238             // management of operator priority
2239             if((nullptr != nodeExecOrChild) && (nodeExec != nodeExecOrChild))
2240             {
2241                 // good 1 1 2 ; bad 1 0 4
2242                 if(nodeExecOrChild->getPriority() >= node->getPriority())
2243                 {
2244                     node->setNextNode(nodeExecOrChild);
2245                     parent->setNextNode(nullptr);
2246                 }
2247             }
2248             else if(node->getPriority() >= nodeExec->getPriority())
2249             {
2250                 node->setNextNode(nodeExec->getNextNode());
2251                 nodeExec->setNextNode(nullptr);
2252             }
2253 
2254             // nodeResult = node;
2255             previous->setNextNode(node);
2256 
2257             result= true;
2258         }
2259         else
2260         {
2261             delete node;
2262         }
2263     }
2264     else
2265     {
2266         while(readOption(str, previous))
2267         {
2268             previous= ParsingToolBox::getLeafNode(previous);
2269             result= true;
2270         }
2271     }
2272     return result;
2273 }
2274 bool ParsingToolBox::readFunction(QString& str, ExecutionNode*& node)
2275 {
2276     for(const auto& kv : m_functionMap)
2277     {
2278         if(str.startsWith(kv.first))
2279         {
2280             str= str.remove(0, kv.first.size());
2281             switch(kv.second)
2282             {
2283             case REPEAT:
2284             {
2285                 auto repeaterNode= new RepeaterNode();
2286                 if(ParsingToolBox::readReaperArguments(repeaterNode, str))
2287                 {
2288                     node= repeaterNode;
2289                 }
2290             }
2291             break;
2292             }
2293         }
2294     }
2295 
2296     if(node == nullptr)
2297         return false;
2298     return true;
2299 }
2300 
2301 bool ParsingToolBox::readNode(QString& str, ExecutionNode*& node)
2302 {
2303     if(str.isEmpty())
2304         return false;
2305 
2306     QString key= str.at(0);
2307     if(m_nodeActionMap.contains(key))
2308     {
2309         JumpBackwardNode* jumpNode= new JumpBackwardNode();
2310         node= jumpNode;
2311         str= str.remove(0, 1);
2312         readOption(str, jumpNode);
2313         return true;
2314     }
2315     return false;
2316 }
2317 
2318 bool ParsingToolBox::readInstructionOperator(QChar c)
2319 {
2320     if(c == ';')
2321     {
2322         return true;
2323     }
2324     return false;
2325 }
2326 
2327 void ParsingToolBox::insertAlias(DiceAlias* dice, int i)
2328 {
2329     if(i >= m_aliasList.size())
2330     {
2331         m_aliasList.insert(i, dice);
2332     }
2333 }
2334 const QList<DiceAlias*>& ParsingToolBox::getAliases() const
2335 {
2336     return m_aliasList;
2337 }
2338 
2339 QList<DiceAlias*>* ParsingToolBox::aliases()
2340 {
2341     return &m_aliasList;
2342 }
2343 
2344 void ParsingToolBox::setAliases(const QList<DiceAlias*> list)
2345 {
2346     qDeleteAll(m_aliasList);
2347     m_aliasList.clear();
2348     m_aliasList= list;
2349 }
2350 
2351 std::vector<ExecutionNode*> ParsingToolBox::readInstructionList(QString& str, bool global)
2352 {
2353     if(str.isEmpty())
2354         return {};
2355 
2356     std::vector<ExecutionNode*> startNodes;
2357 
2358     bool hasInstruction= false;
2359     bool readInstruction= true;
2360     while(readInstruction)
2361     {
2362         ExecutionNode* startNode= nullptr;
2363         bool keepParsing= readExpression(str, startNode);
2364         if(nullptr != startNode)
2365         {
2366             hasInstruction= true;
2367             startNodes.push_back(startNode);
2368             auto latest= startNode;
2369             if(keepParsing)
2370             {
2371                 latest= ParsingToolBox::getLeafNode(latest);
2372                 keepParsing= !str.isEmpty();
2373                 while(keepParsing)
2374                 {
2375                     auto before= str;
2376                     if(readOperator(str, latest))
2377                     {
2378                         latest= ParsingToolBox::getLeafNode(latest);
2379                     }
2380                     keepParsing= (!str.isEmpty() && (before != str));
2381                 }
2382             }
2383             if(!str.isEmpty() && readInstructionOperator(str[0]))
2384             {
2385                 str= str.remove(0, 1);
2386             }
2387             else
2388             {
2389                 QString result;
2390                 QString comment;
2391                 if(readComment(str, result, comment))
2392                 {
2393                     m_comment= result;
2394                 }
2395                 readInstruction= false;
2396             }
2397         }
2398         else
2399         {
2400             readInstruction= false;
2401         }
2402     }
2403     if(global)
2404         m_startNodes= startNodes;
2405     return startNodes;
2406 }
2407 
2408 SubtituteInfo ParsingToolBox::readVariableFromString(const QString& source, int& start)
2409 {
2410     bool found= false;
2411     SubtituteInfo info;
2412     int i= start;
2413     for(; i >= 0 && !found; --i)
2414     {
2415         if(source.at(i) == '$')
2416         {
2417             auto rest= source.mid(i + 1, 1 + start - i);
2418             qint64 number;
2419             if(readNumber(rest, number))
2420             {
2421                 auto len= QString::number(number).size() - 1;
2422                 readSubtitutionParameters(info, rest);
2423                 info.setLength(info.length() + len);
2424                 info.setResultIndex(static_cast<int>(number));
2425                 info.setPosition(i);
2426                 found= true;
2427             }
2428         }
2429     }
2430     start= i;
2431     return info;
2432 }
2433 
2434 SubtituteInfo ParsingToolBox::readPlaceHolderFromString(const QString& source, int& start)
2435 {
2436     bool found= false;
2437     SubtituteInfo info;
2438     int i= start;
2439     for(; i >= 0 && !found; --i)
2440     {
2441         if(source.at(i) == '@')
2442         {
2443             auto rest= source.mid(i + 1, 1 + start - i);
2444             qint64 number;
2445             if(readNumber(rest, number))
2446             {
2447                 auto len= QString::number(number).size() - 1;
2448                 readSubtitutionParameters(info, rest);
2449                 info.setLength(info.length() + len);
2450                 info.setResultIndex(static_cast<int>(number));
2451                 info.setPosition(i);
2452                 found= true;
2453             }
2454         }
2455     }
2456     start= i;
2457     return info;
2458 }
2459 
2460 QString ParsingToolBox::number(qreal value)
2461 {
2462     if(value > 1000000)
2463         return QString::number(value, 'f', 20);
2464     else
2465         return QString::number(value);
2466 }
2467 
2468 ExportedDiceResult ParsingToolBox::finalDiceResultFromInstruction(ExecutionNode* start)
2469 {
2470     ExecutionNode* next= ParsingToolBox::getLeafNode(start);
2471     Result* result= next->getResult();
2472     ExportedDiceResult nodeResult;
2473     std::set<QString> alreadyAdded;
2474     while(nullptr != result)
2475     {
2476         if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST))
2477         {
2478             DiceResult* diceResult= dynamic_cast<DiceResult*>(result);
2479             QList<HighLightDice> list;
2480             quint64 faces= 0;
2481             for(auto& die : diceResult->getResultList())
2482             {
2483                 faces= die->getFaces();
2484                 HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(),
2485                                      die->hasBeenDisplayed(), die->getFaces(), die->getUuid());
2486                 if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end() && !hlDice.displayed())
2487                 {
2488                     list.append(hlDice);
2489                     alreadyAdded.insert(die->getUuid());
2490                 }
2491             }
2492             if(!list.isEmpty())
2493             {
2494                 auto vals= nodeResult.value(faces);
2495                 vals.append(list);
2496                 nodeResult.insert(faces, vals);
2497             }
2498         }
2499         /*if(nodeResult.isEmpty())
2500             result= result->getPrevious();
2501         else*/
2502         result= result->getPrevious();
2503     }
2504     return nodeResult;
2505 }
2506 
2507 ExportedDiceResult ParsingToolBox::allDiceResultFromInstruction(ExecutionNode* start)
2508 {
2509     ExecutionNode* next= ParsingToolBox::getLeafNode(start);
2510     Result* result= next->getResult();
2511     ExportedDiceResult nodeResult;
2512     std::set<QString> alreadyAdded;
2513     while(nullptr != result)
2514     {
2515         if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST))
2516         {
2517             DiceResult* diceResult= dynamic_cast<DiceResult*>(result);
2518             QList<HighLightDice> list;
2519             quint64 faces= 0;
2520             for(auto& die : diceResult->getResultList())
2521             {
2522                 faces= die->getFaces();
2523                 HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(),
2524                                      die->hasBeenDisplayed(), die->getFaces(), die->getUuid());
2525                 if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end())
2526                 {
2527                     list.append(hlDice);
2528                     alreadyAdded.insert(die->getUuid());
2529                 }
2530             }
2531             if(!list.isEmpty())
2532             {
2533                 auto vals= nodeResult.value(faces);
2534                 vals.append(list);
2535                 nodeResult.insert(faces, vals);
2536             }
2537         }
2538         result= result->getPrevious();
2539     }
2540     return nodeResult;
2541 }
2542 
2543 void ParsingToolBox::addResultInJson(QJsonObject& obj, Dice::RESULT_TYPE type, const QString& key, ExecutionNode* start,
2544                                      bool b)
2545 {
2546     auto pair= hasResultOfType(type, start, b);
2547     if(pair.first)
2548         obj[key]= QJsonValue::fromVariant(pair.second);
2549 }
2550 
2551 void ParsingToolBox::addDiceResultInJson(
2552     QJsonObject& obj, ExecutionNode* start,
2553     std::function<QString(const QString& value, const QString& color, bool highlighted)> colorize)
2554 {
2555     QJsonArray diceValues;
2556     auto result= ParsingToolBox::allDiceResultFromInstruction(start);
2557     for(auto listOfList : result.values())
2558     {
2559         for(auto listDiceResult : listOfList)
2560         {
2561             for(auto hlDice : listDiceResult)
2562             {
2563                 QJsonObject diceObj;
2564                 diceObj["face"]= static_cast<qreal>(hlDice.faces());
2565                 diceObj["color"]= hlDice.color();
2566                 diceObj["displayed"]= hlDice.displayed();
2567                 diceObj["string"]= colorize(hlDice.getResultString(), hlDice.color(), hlDice.isHighlighted());
2568                 diceObj["highlight"]= hlDice.isHighlighted();
2569                 diceObj["uuid"]= hlDice.uuid();
2570                 auto val= hlDice.result();
2571                 if(!val.isEmpty())
2572                 {
2573                     diceObj["value"]= std::accumulate(val.begin(), val.end(), 0);
2574                     if(val.size() > 1)
2575                     {
2576                         QJsonArray intValues;
2577                         std::transform(val.begin(), val.end(), std::back_inserter(intValues),
2578                                        [](qint64 val) { return static_cast<int>(val); });
2579                         diceObj["subvalues"]= intValues;
2580                     }
2581                 }
2582                 diceValues.append(diceObj);
2583             }
2584         }
2585     }
2586     if(!diceValues.isEmpty())
2587         obj["diceval"]= diceValues;
2588 }
2589 
2590 SubtituteInfo::SubtituteInfo() {}
2591 
2592 bool SubtituteInfo::isValid() const
2593 {
2594     return !(m_position + m_resultIndex < 0);
2595 }
2596 
2597 int SubtituteInfo::length() const
2598 {
2599     return m_length;
2600 }
2601 
2602 void SubtituteInfo::setLength(int length)
2603 {
2604     m_length= length;
2605 }
2606 
2607 int SubtituteInfo::resultIndex() const
2608 {
2609     return m_resultIndex;
2610 }
2611 
2612 void SubtituteInfo::setResultIndex(int valueIndex)
2613 {
2614     m_resultIndex= valueIndex;
2615 }
2616 
2617 int SubtituteInfo::position() const
2618 {
2619     return m_position;
2620 }
2621 
2622 void SubtituteInfo::setPosition(int position)
2623 {
2624     m_position= position;
2625 }
2626 
2627 int SubtituteInfo::digitNumber() const
2628 {
2629     return m_digitNumber;
2630 }
2631 
2632 void SubtituteInfo::setDigitNumber(int digitNumber)
2633 {
2634     m_digitNumber= digitNumber;
2635 }
2636 
2637 int SubtituteInfo::subIndex() const
2638 {
2639     return m_subIndex;
2640 }
2641 
2642 void SubtituteInfo::setSubIndex(int subindex)
2643 {
2644     m_subIndex= subindex;
2645 }