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 }