diff options
Diffstat (limited to 'src/libparser/parsingtoolbox.cpp')
| -rw-r--r-- | src/libparser/parsingtoolbox.cpp | 2641 |
1 files changed, 2641 insertions, 0 deletions
diff --git a/src/libparser/parsingtoolbox.cpp b/src/libparser/parsingtoolbox.cpp new file mode 100644 index 0000000..dfde37f --- /dev/null +++ b/src/libparser/parsingtoolbox.cpp @@ -0,0 +1,2641 @@ +/*************************************************************************** + * Copyright (C) 2014 by Renaud Guezennec * + * https://rolisteam.org/contact * + * * + * This file is part of DiceParser * + * * + * DiceParser is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "diceparser/parsingtoolbox.h" + +#include <QDebug> +#include <QJsonArray> +#include <QJsonObject> +#include <QRegularExpression> +#include <QString> +#include <set> + +#include "node/allsamenode.h" +#include "node/bind.h" +#include "node/countexecutenode.h" +#include "node/dicerollernode.h" +#include "node/executionnode.h" +#include "node/explodedicenode.h" +#include "node/filternode.h" +#include "node/groupnode.h" +#include "node/helpnode.h" +#include "node/ifnode.h" +#include "node/jumpbackwardnode.h" +#include "node/keepdiceexecnode.h" +#include "node/listaliasnode.h" +#include "node/listsetrollnode.h" +#include "node/mergenode.h" +#include "node/numbernode.h" +#include "node/occurencecountnode.h" +#include "node/paintnode.h" +#include "node/parenthesesnode.h" +#include "node/repeaternode.h" +#include "node/replacevaluenode.h" +#include "node/rerolldicenode.h" +#include "node/scalaroperatornode.h" +#include "node/sortresult.h" +#include "node/splitnode.h" +#include "node/startingnode.h" +#include "node/stringnode.h" +#include "node/switchcasenode.h" +#include "node/uniquenode.h" +#include "node/valueslistnode.h" +#include "node/variablenode.h" +#include "operationcondition.h" +#include "range.h" +#include "validatorlist.h" + +QHash<QString, QString> ParsingToolBox::m_variableHash; + +ParsingToolBox::ParsingToolBox() +{ + // m_logicOp = ; + m_logicOp.insert(">=", Dice::CompareOperator::GreaterOrEqual); + m_logicOp.insert("<=", Dice::CompareOperator::LesserOrEqual); + m_logicOp.insert("<", Dice::CompareOperator::LesserThan); + m_logicOp.insert("=", Dice::CompareOperator::Equal); + m_logicOp.insert(">", Dice::CompareOperator::GreaterThan); + m_logicOp.insert("!=", Dice::CompareOperator::Different); + + // m_logicOperation = ; + m_logicOperation.insert("|", Dice::LogicOperation::OR); + m_logicOperation.insert("^", Dice::LogicOperation::EXCLUSIVE_OR); + m_logicOperation.insert("&", Dice::LogicOperation::AND); + + // m_conditionOperation = ; + m_conditionOperation.insert("%", Dice::ConditionOperator::Modulo); + + // m_arithmeticOperation = new QHash<QString,ScalarOperatorNode::ArithmeticOperator>(); + m_arithmeticOperation.push_back({QStringLiteral("**"), Dice::ArithmeticOperator::POW}); + m_arithmeticOperation.push_back({QStringLiteral("+"), Dice::ArithmeticOperator::PLUS}); + m_arithmeticOperation.push_back({QStringLiteral("-"), Dice::ArithmeticOperator::MINUS}); + m_arithmeticOperation.push_back({QStringLiteral("*"), Dice::ArithmeticOperator::MULTIPLICATION}); + m_arithmeticOperation.push_back({QStringLiteral("x"), Dice::ArithmeticOperator::MULTIPLICATION}); + m_arithmeticOperation.push_back({QStringLiteral("|"), Dice::ArithmeticOperator::INTEGER_DIVIDE}); + m_arithmeticOperation.push_back({QStringLiteral("/"), Dice::ArithmeticOperator::DIVIDE}); + m_arithmeticOperation.push_back({QStringLiteral("÷"), Dice::ArithmeticOperator::DIVIDE}); + + m_mapDiceOp.insert(QStringLiteral("D"), D); + m_mapDiceOp.insert(QStringLiteral("L"), L); + + m_OptionOp.insert(QStringLiteral("k"), Keep); + m_OptionOp.insert(QStringLiteral("K"), KeepAndExplode); + m_OptionOp.insert(QStringLiteral("s"), Sort); + m_OptionOp.insert(QStringLiteral("c"), Count); + m_OptionOp.insert(QStringLiteral("r"), Reroll); + m_OptionOp.insert(QStringLiteral("e"), Explode); + m_OptionOp.insert(QStringLiteral("R"), RerollUntil); + m_OptionOp.insert(QStringLiteral("a"), RerollAndAdd); + m_OptionOp.insert(QStringLiteral("m"), Merge); + m_OptionOp.insert(QStringLiteral("i"), ifOperator); + m_OptionOp.insert(QStringLiteral("p"), Painter); + m_OptionOp.insert(QStringLiteral("f"), Filter); + m_OptionOp.insert(QStringLiteral("y"), Split); + m_OptionOp.insert(QStringLiteral("u"), Unique); + m_OptionOp.insert(QStringLiteral("t"), AllSameExplode); + m_OptionOp.insert(QStringLiteral("g"), Group); + m_OptionOp.insert(QStringLiteral("b"), Bind); + m_OptionOp.insert(QStringLiteral("o"), Occurences); + m_OptionOp.insert(QStringLiteral("S"), SwitchCaseOption); + m_OptionOp.insert(QStringLiteral("T"), TransformOption); + + m_functionMap.insert({QStringLiteral("repeat"), REPEAT}); + + m_nodeActionMap.insert(QStringLiteral("@"), JumpBackward); + + m_commandList.append(QStringLiteral("help")); + m_commandList.append(QStringLiteral("la")); +} + +ParsingToolBox::ParsingToolBox(const ParsingToolBox&) {} +ParsingToolBox::~ParsingToolBox() {} + +void ParsingToolBox::clearUp() +{ + m_errorMap.clear(); + m_comment= QString(""); +} + +void ParsingToolBox::cleanUpAliases() +{ + m_aliasList.clear(); +} + +ExecutionNode* ParsingToolBox::addSort(ExecutionNode* e, bool b) +{ + SortResultNode* nodeSort= new SortResultNode(); + nodeSort->setSortAscending(b); + if(nullptr != e) + e->setNextNode(nodeSort); + return nodeSort; +} + +void ParsingToolBox::addError(Dice::ERROR_CODE code, const QString& msg) +{ + m_errorMap.insert(code, msg); +} +void ParsingToolBox::addWarning(Dice::ERROR_CODE code, const QString& msg) +{ + m_warningMap.insert(code, msg); +} +bool ParsingToolBox::readDiceLogicOperator(QString& str, Dice::ConditionOperator& op) +{ + QString longKey; + auto const& keys= m_conditionOperation.keys(); + for(const QString& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_conditionOperation.value(longKey); + return true; + } + + return false; +} + +bool ParsingToolBox::readArithmeticOperator(QString& str, Dice::ArithmeticOperator& op) +{ + + auto it= std::find_if(m_arithmeticOperation.begin(), m_arithmeticOperation.end(), + [str](const std::pair<QString, Dice::ArithmeticOperator>& pair) + { return str.startsWith(pair.first); }); + if(it == m_arithmeticOperation.end()) + return false; + + op= it->second; + str= str.remove(0, it->first.size()); + return true; +} + +bool ParsingToolBox::readLogicOperator(QString& str, Dice::CompareOperator& op) +{ + QString longKey; + auto const& keys= m_logicOp.keys(); + for(const QString& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_logicOp.value(longKey); + return true; + } + + return false; +} +QString ParsingToolBox::getComment() const +{ + return m_comment; +} + +void ParsingToolBox::setComment(const QString& comment) +{ + m_comment= comment; +} +const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getErrorList() const +{ + return m_errorMap; +} +const QMap<Dice::ERROR_CODE, QString>& ParsingToolBox::getWarningList() const +{ + return m_warningMap; +} +bool ParsingToolBox::readOperand(QString& str, ExecutionNode*& node) +{ + qint64 intValue= 1; + QString resultStr; + if(readDynamicVariable(str, intValue)) + { + VariableNode* variableNode= new VariableNode(); + variableNode->setIndex(static_cast<quint64>(intValue - 1)); + variableNode->setData(&m_startNodes); + node= variableNode; + return true; + } + else if(readNumber(str, intValue)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(intValue); + node= numberNode; + return true; + } + else if(readString(str, resultStr)) + { + StringNode* strNode= new StringNode(); + strNode->setString(resultStr); + node= strNode; + return true; + } + return false; +} + +Validator* ParsingToolBox::readValidator(QString& str, bool hasSquare) +{ + Validator* returnVal= nullptr; + auto opCompare= readConditionType(str); + Dice::CompareOperator myLogicOp{Dice::CompareOperator::Equal}; + readLogicOperator(str, myLogicOp); + + Dice::ConditionOperator condiOp{Dice::ConditionOperator::Modulo}; + bool hasDiceLogicOperator= readDiceLogicOperator(str, condiOp); + ExecutionNode* operandNode= nullptr; + if(hasDiceLogicOperator) + { + if(readOperand(str, operandNode)) + { + OperationCondition* condition= new OperationCondition(); + condition->setConditionType(opCompare); + condition->setValueNode(operandNode); + Validator* valid= readValidator(str, hasSquare); + BooleanCondition* boolC= dynamic_cast<BooleanCondition*>(valid); + if(nullptr != boolC) + { + condition->setBoolean(boolC); + returnVal= condition; + } + else + { + delete condition; + } + } + } + else if(readOperand(str, operandNode)) + { + bool isRange= false; + if(str.startsWith("..") && hasSquare) + { + str= str.remove(0, 2); + qint64 end= 0; + if(readNumber(str, end)) + { + str= str.remove(0, 1); + qint64 start= operandNode->getScalarResult(); + Range* range= new Range(); + range->setConditionType(opCompare); + range->setValue(start, end); + returnVal= range; + isRange= true; + } + } + + if(!isRange) + { + BooleanCondition* condition= new BooleanCondition(); + condition->setConditionType(opCompare); + condition->setValueNode(operandNode); + condition->setOperator(myLogicOp); + returnVal= condition; + } + } + return returnVal; +} + +Dice::ConditionType ParsingToolBox::readConditionType(QString& str) +{ + Dice::ConditionType type= Dice::OnEach; + if(str.startsWith('.')) + { + str= str.remove(0, 1); + type= Dice::OneOfThem; + } + else if(str.startsWith('?')) + { + str= str.remove(0, 1); + type= Dice::OnEachValue; + } + else if(str.startsWith('*')) + { + str= str.remove(0, 1); + type= Dice::AllOfThem; + } + else if(str.startsWith(':')) + { + str= str.remove(0, 1); + type= Dice::OnScalar; + } + return type; +} +bool ParsingToolBox::hasError() const +{ + return !m_errorMap.isEmpty(); +} +ValidatorList* ParsingToolBox::readValidatorList(QString& str) +{ + bool expectSquareBrasket= false; + if((str.startsWith("["))) + { + str= str.remove(0, 1); + expectSquareBrasket= true; + } + Validator* tmp= readValidator(str, expectSquareBrasket); + Dice::LogicOperation opLogic; + + QVector<Dice::LogicOperation> operators; + QList<Validator*> validatorList; + + while(nullptr != tmp) + { + bool hasOperator= readLogicOperation(str, opLogic); + if(hasOperator) + { + operators.append(opLogic); + validatorList.append(tmp); + tmp= readValidator(str, expectSquareBrasket); + } + else + { + if((expectSquareBrasket) && (str.startsWith("]"))) + { + str= str.remove(0, 1); + // isOk=true; + } + validatorList.append(tmp); + tmp= nullptr; + } + } + if(!validatorList.isEmpty()) + { + ValidatorList* validator= new ValidatorList(); + validator->setOperationList(operators); + validator->setValidators(validatorList); + return validator; + } + else + { + return nullptr; + } +} +bool ParsingToolBox::readLogicOperation(QString& str, Dice::LogicOperation& op) +{ + QString longKey; + auto const& keys= m_logicOperation.keys(); + for(auto& tmp : keys) + { + if(str.startsWith(tmp)) + { + if(longKey.size() < tmp.size()) + { + longKey= tmp; + } + } + } + if(longKey.size() > 0) + { + str= str.remove(0, longKey.size()); + op= m_logicOperation.value(longKey); + return true; + } + + return false; +} + +bool ParsingToolBox::readNumber(QString& str, qint64& myNumber) +{ + if(str.isEmpty()) + return false; + + QString number; + int i= 0; + while(i < str.length() && ((str[i].isNumber()) || ((i == 0) && (str[i] == '-')))) + { + number+= str[i]; + ++i; + } + + if(number.isEmpty()) + { + QString reason; + return readVariable(str, myNumber, reason); + } + + bool ok; + myNumber= number.toLongLong(&ok); + if(ok) + { + str= str.remove(0, number.size()); + return true; + } + + return false; +} +bool ParsingToolBox::readDynamicVariable(QString& str, qint64& index) +{ + if(str.isEmpty()) + return false; + if(str.startsWith('$')) + { + QString number; + int i= 1; + while(i < str.length() && (str[i].isNumber())) + { + number+= str[i]; + ++i; + } + + bool ok; + index= number.toLongLong(&ok); + if(ok) + { + str= str.remove(0, number.size() + 1); + return true; + } + } + return false; +} + +ExecutionNode* ParsingToolBox::getLeafNode(ExecutionNode* start) +{ + if(nullptr == start) + return nullptr; + + ExecutionNode* next= start; + while(nullptr != next->getNextNode()) + { + next= next->getNextNode(); + } + return next; +} + +const std::vector<ExecutionNode*>& ParsingToolBox::getStartNodes() +{ + return m_startNodes; +} + +QStringList ParsingToolBox::allFirstResultAsString(bool& hasAlias) const +{ + // QStringList allResult; + QStringList stringListResult; + for(auto node : m_startNodes) + { + QVariant var; + auto stringPair= hasResultOfType(Dice::RESULT_TYPE::STRING, node); + auto scalarPair= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node); + if(stringPair.first) + { + stringListResult << stringPair.second.toString(); + hasAlias= true; + } + else if(scalarPair.first) + { + stringListResult << number(scalarPair.second.toReal()); + hasAlias= true; + } + } + return stringListResult; +} +std::pair<bool, QVariant> ParsingToolBox::hasResultOfType(Dice::RESULT_TYPE type, ExecutionNode* node, + bool notthelast) const +{ + bool hasValidResult= false; + QVariant var; + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + while((result != nullptr) && (!hasValidResult)) + { + bool lastResult= false; + if(notthelast) + lastResult= (nullptr == result->getPrevious()); + + if(result->hasResultOfType(type) && !lastResult) + { + hasValidResult= true; + var= result->getResult(type); + } + result= result->getPrevious(); + } + return {hasValidResult, var}; +} + +QList<qreal> ParsingToolBox::scalarResultsFromEachInstruction() const +{ + QList<qreal> resultValues; + std::set<QString> alreadyVisitedNode; + for(auto node : m_startNodes) + { + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + bool scalarDone= false; + while((result != nullptr) && (!scalarDone)) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::SCALAR)) + { + if(alreadyVisitedNode.find(result->getId()) == alreadyVisitedNode.end()) + { + resultValues << result->getResult(Dice::RESULT_TYPE::SCALAR).toReal(); + alreadyVisitedNode.insert(result->getId()); + } + scalarDone= true; + } + result= result->getPrevious(); + } + } + return resultValues; +} + +QList<qreal> ParsingToolBox::sumOfDiceResult() const +{ + QList<qreal> resultValues; + for(auto node : m_startNodes) + { + qreal resultValue= 0; + ExecutionNode* next= ParsingToolBox::getLeafNode(node); + Result* result= next->getResult(); + bool found= false; + while((nullptr != result) && (!found)) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* myDiceResult= dynamic_cast<DiceResult*>(result); + if(nullptr != myDiceResult) + { + for(auto& die : myDiceResult->getResultList()) + { + resultValue+= die->getValue(); + } + found= true; + } + } + result= result->getPrevious(); + } + resultValues << resultValue; + } + return resultValues; +} + +std::pair<QString, QString> ParsingToolBox::finalScalarResult() const +{ + QString scalarText; + QString lastScalarText; + auto listDie= diceResultFromEachInstruction(); + if(hasIntegerResultNotInFirst()) + { + QStringList strLst; + auto listScalar= scalarResultsFromEachInstruction(); + for(auto val : listScalar) + { + strLst << number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); + lastScalarText= strLst.last(); + } + else if(!listDie.isEmpty()) + { + auto values= sumOfDiceResult(); + QStringList strLst; + for(auto val : values) + { + strLst << number(val); + } + scalarText= QString("%1").arg(strLst.join(',')); + } + return {scalarText, lastScalarText}; +} + +bool ParsingToolBox::hasIntegerResultNotInFirst() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::SCALAR, node, true).first; + } + return result; +} + +bool ParsingToolBox::hasDiceResult() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::DICE_LIST, node).first; + } + return result; +} +bool ParsingToolBox::hasStringResult() const +{ + bool result= false; + for(auto node : m_startNodes) + { + result|= hasResultOfType(Dice::RESULT_TYPE::STRING, node).first; + } + return result; +} + +QList<ExportedDiceResult> ParsingToolBox::diceResultFromEachInstruction() const +{ + QList<ExportedDiceResult> resultList; + for(auto start : m_startNodes) + { + resultList.append(ParsingToolBox::finalDiceResultFromInstruction(start)); + } + return resultList; +} + +QStringList listOfDiceResult(const QList<ExportedDiceResult>& list, bool removeDouble= false) +{ + QStringList listOfDiceResult; + std::set<QString> alreadyAdded; + for(auto map : list) + { + for(auto key : map.keys()) + { + auto listOfList= map.value(key); + for(auto dice : listOfList) + { + QString stringVal; + for(auto val : dice) + { + if(removeDouble && (alreadyAdded.end() != alreadyAdded.find(val.uuid()))) + continue; + + alreadyAdded.insert(val.uuid()); + + qint64 total= 0; + QStringList dicelist; + for(auto score : val.result()) + { + total+= score; + dicelist << QString::number(score); + } + if(val.result().size() > 1) + { + stringVal= QString("%1 [%2]").arg(total).arg(dicelist.join(',')); + listOfDiceResult << stringVal; + } + else + { + listOfDiceResult << QString::number(total); + } + } + } + } + } + return listOfDiceResult; +} + +QString ParsingToolBox::finalStringResult(std::function<QString(const QString&, const QString&, bool)> colorize, + bool removeUnhighlighted) const +{ + bool ok; + QStringList allStringlist= allFirstResultAsString(ok); + auto listFull= diceResultFromEachInstruction(); + + QStringList resultWithPlaceHolder; + std::for_each(allStringlist.begin(), allStringlist.end(), + [&resultWithPlaceHolder](const QString& sub) + { + QRegularExpression ex("%[1-3]?|\\$[1-9]+|@[1-9]+"); + if(sub.contains(ex)) + resultWithPlaceHolder.append(sub); + }); + auto stringResult= resultWithPlaceHolder.isEmpty() ? allStringlist.join(",") : resultWithPlaceHolder.join(","); + + auto pairScalar= finalScalarResult(); + + stringResult.replace("%1", pairScalar.first); + stringResult.replace("%2", listOfDiceResult(diceResultFromEachInstruction(), true).join(",").trimmed()); + stringResult.replace("%3", pairScalar.second); + stringResult.replace("\\n", "\n"); + + QMap<Dice::ERROR_CODE, QString> errorMap; + stringResult= ParsingToolBox::replaceVariableToValue(stringResult, allStringlist, errorMap); + stringResult= ParsingToolBox::replacePlaceHolderToValue(stringResult, listFull, removeUnhighlighted, colorize); + + return stringResult; +} + +bool ParsingToolBox::readString(QString& str, QString& strResult) +{ + if(str.isEmpty()) + return false; + + if(str.startsWith('"')) + { + str= str.remove(0, 1); + + int i= 0; + int j= 0; + bool previousEscape= false; + QString result; + /*&& + (((!previousEscape) && !(str[i]=='"')) || (previousEscape) && !(str[i]=='"')) + || (str[i]=='\\'))*/ + while(i < str.length() && (!(!previousEscape && (str[i] == '"')) || (previousEscape && str[i] != '"'))) + { + if(str[i] == '\\') + { + previousEscape= true; + } + else + { + if(previousEscape && str[i] != '\"') + { + result+= '\\'; + ++j; + } + result+= str[i]; + previousEscape= false; + } + ++i; + } + + if(!result.isEmpty()) + { + str= str.remove(0, i); + strResult= result; + if(str.startsWith('"')) + { + str= str.remove(0, 1); + return true; + } + } + } + + return false; +} + +bool ParsingToolBox::readVariable(QString& str, qint64& myNumber, QString& reasonFail) +{ + if(str.isEmpty()) + return false; + + if(str.startsWith("${")) + { + str= str.remove(0, 2); + } + QString key; + int post= str.indexOf('}'); + key= str.left(post); + + if(!m_variableHash.isEmpty()) + { + if(m_variableHash.contains(key)) + { + QString value= m_variableHash.value(key); + bool ok; + int valueInt= value.toInt(&ok); + if(ok) + { + myNumber= valueInt; + str= str.remove(0, post + 1); + return true; + } + else + { + reasonFail= QStringLiteral("Variable value is %1, not a number").arg(value); + } + } + else + { + reasonFail= QStringLiteral("Variable not found"); + } + } + else + { + reasonFail= QStringLiteral("No Variables are defined"); + } + + return false; +} +bool ParsingToolBox::readComma(QString& str) +{ + if(str.startsWith(",")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} +bool ParsingToolBox::readOpenParentheses(QString& str) +{ + if(str.startsWith("(")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} + +bool ParsingToolBox::readCloseParentheses(QString& str) +{ + if(str.startsWith(")")) + { + str= str.remove(0, 1); + return true; + } + else + return false; +} + +int ParsingToolBox::findClosingCharacterIndexOf(QChar open, QChar closing, const QString& str, int offset) +{ + int counter= offset; + int i= 0; + for(auto const& letter : str) + { + if(letter == open) + ++counter; + else if(letter == closing) + --counter; + + if(counter == 0) + return i; + + ++i; + } + return -1; +} + +bool ParsingToolBox::readList(QString& str, QStringList& list, QList<Range>& ranges) +{ + if(str.startsWith("[")) + { + str= str.remove(0, 1); + int pos= findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]"); + if(-1 != pos) + { + QString liststr= str.left(pos); + list= liststr.split(","); + str= str.remove(0, pos + 1); + readProbability(list, ranges); + return true; + } + } + return false; +} +bool ParsingToolBox::readAscending(QString& str) +{ + if(str.isEmpty()) + { + return false; + } + else if(str.at(0) == 'l') + { + str= str.remove(0, 1); + return true; + } + return false; +} + +bool ParsingToolBox::readStopAtFirst(QString& str) +{ + if(str.isEmpty()) + return false; + else if(str.at(0) == '^') + { + str= str.remove(0, 1); + return true; + } + return false; +} + +Dice::CONDITION_STATE ParsingToolBox::isValidValidator(ExecutionNode* previous, ValidatorList* val) +{ + DiceRollerNode* node= getDiceRollerNode(previous); + if(nullptr == node) + return Dice::CONDITION_STATE::ERROR_STATE; + + return val->isValidRangeSize(node->getRange()); +} +DiceRollerNode* ParsingToolBox::getDiceRollerNode(ExecutionNode* previous) +{ + while(nullptr != previous) + { + DiceRollerNode* node= dynamic_cast<DiceRollerNode*>(previous); + if(nullptr != node) + { + return node; + } + previous= previous->getPreviousNode(); + } + return nullptr; +} +bool ParsingToolBox::readDiceRange(QString& str, qint64& start, qint64& end) +{ + bool expectSquareBrasket= false; + + if((str.startsWith("["))) + { + str= str.remove(0, 1); + expectSquareBrasket= true; + } + if(readNumber(str, start)) + { + if(str.startsWith("..")) + { + str= str.remove(0, 2); + if(readNumber(str, end)) + { + if(expectSquareBrasket) + { + if(str.startsWith("]")) + { + str= str.remove(0, 1); + return true; + } + } + } + } + else if(expectSquareBrasket) + { + if(str.startsWith("]")) + { + str= str.remove(0, 1); + end= start; + return true; + } + } + } + return false; +} +ParsingToolBox::LIST_OPERATOR ParsingToolBox::readListOperator(QString& str) +{ + QHash<QChar, ParsingToolBox::LIST_OPERATOR> hash; + hash.insert('u', UNIQUE); + hash.insert('n', NOCOMMA); + bool findOne= false; + ParsingToolBox::LIST_OPERATOR op= NONE; + int i= 0; + do + { + auto keys= hash.keys(); + findOne= false; + for(auto const& key : qAsConst(keys)) + { + if(str.startsWith(key)) + { + str= str.remove(0, 1); + op= hash[key]; + findOne= true; + ++i; + } + } + } while(findOne); + + return i > 1 ? UniqueAndNoComma : op; +} + +bool ParsingToolBox::readPainterParameter(PainterNode* painter, QString& str) +{ + if(!str.startsWith('[')) + return false; + + str= str.remove(0, 1); + int pos= str.indexOf(']'); + + if(pos == -1) + return false; + + QString data= str.left(pos); + str= str.remove(0, pos + 1); + QStringList duos= data.split(','); + bool result= false; + for(QString& duoStr : duos) + { + QStringList keyValu= duoStr.split(':'); + if(keyValu.size() == 2) + { + painter->insertColorItem(keyValu[1], keyValu[0].toInt()); + result= true; + } + } + + return result; +} + +QHash<QString, QString> ParsingToolBox::getVariableHash() +{ + return m_variableHash; +} + +void ParsingToolBox::setVariableHash(const QHash<QString, QString>& variableHash) +{ + m_variableHash= variableHash; +} + +void ParsingToolBox::setStartNodes(std::vector<ExecutionNode*> nodes) +{ + m_startNodes= nodes; +} + +void ParsingToolBox::readProbability(QStringList& str, QList<Range>& ranges) +{ + quint64 totalDistance= 0; + quint64 undefDistance= 0; + int undefCount= 0; + int maxValue= 0; + int i= 0; + int j= 0; + bool hasPercentage= false; + for(QString line : str) + { + int pos= line.indexOf('['); + if(-1 != pos) + { + QString rangeStr= line.right(line.length() - pos); + line= line.left(pos); + str[j]= line; + qint64 start= 0; + qint64 end= 0; + if(readDiceRange(rangeStr, start, end)) + { + Range range; + range.setValue(start, end); + ranges.append(range); + totalDistance+= static_cast<quint64>(end - start + 1); + ++i; + } + else // percentage + { + hasPercentage= true; + Range range; + range.setStart(start); + ranges.append(range); + ++undefCount; + undefDistance+= static_cast<quint64>(start); + } + if((end > maxValue) || (i == 1)) + { + maxValue= static_cast<int>(end); + } + } + else + { + Range range; + range.setEmptyRange(true); + ranges.append(range); + } + ++j; + } + + if((hasPercentage) && (undefDistance != 0)) + { + qreal ratio= 100.0 / static_cast<qreal>(undefDistance); + qint64 realStart= 0; + for(int i= 0; i < ranges.size(); ++i) + { + Range tmp= ranges.at(i); + if(!tmp.isFullyDefined()) + { + int dist= static_cast<int>(tmp.getStart()); + tmp.setStart(realStart + 1); + double truc= dist * ratio; + + tmp.setEnd(static_cast<int>(realStart + truc)); + realStart= tmp.getEnd(); + ranges[i]= tmp; + } + } + } + else + { + int limitUp= 1; + for(int i= 0; i < ranges.size(); ++i) + { + Range range= ranges.at(i); + if(range.isEmptyRange()) + { + range.setStart(limitUp); + range.setEnd(limitUp); + range.setEmptyRange(false); + } + else + { + qint64 sizeRange= range.getEnd() - range.getStart(); + range.setStart(limitUp); + limitUp+= sizeRange; + range.setEnd(limitUp); + } + ++limitUp; + ranges[i]= range; + } + } +} +bool ParsingToolBox::readComment(QString& str, QString& result, QString& comment) +{ + QString left= str; + str= str.trimmed(); + if(str.startsWith("#")) + { + comment= left; + str= str.remove(0, 1); + result= str.trimmed(); + str= ""; + return true; + } + return false; +} + +QString ParsingToolBox::replaceVariableToValue(const QString& source, QStringList values, + QMap<Dice::ERROR_CODE, QString>& errorMap) +{ + QString result= source; + + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readVariableFromString(source, start); + if(ref.resultIndex() > values.size()) + { + auto error= QString("No valid value at index: $%1").arg(ref.resultIndex()); + errorMap.insert(Dice::ERROR_CODE::INVALID_INDEX, error); + return error; + } + + valid= ref.isValid(); + if(!valid) + continue; + + result.remove(ref.position(), ref.length()); + auto val= values[ref.resultIndex() - 1]; + + if(ref.subIndex() >= 0) + { + auto valSplit= val.split(","); + if(ref.subIndex() < valSplit.size()) + val= valSplit[ref.subIndex()]; + } + + if(ref.digitNumber() != 0) + { + auto realVal= QString("%1").arg(val, ref.digitNumber(), QChar('0')); + result.insert(ref.position(), realVal); + } + else + { + result.insert(ref.position(), val); + } + } while(valid); + + return result; +} + +QString ParsingToolBox::replacePlaceHolderFromJson(const QString& source, const QJsonObject& obj) +{ + QStringList resultList; + auto instructions= obj["instructions"].toArray(); + std::vector<std::vector<std::pair<int, QList<QStringList>>>> instructionResult; + for(auto inst : qAsConst(instructions)) + { + std::vector<std::pair<int, QList<QStringList>>> map; + auto obj= inst.toObject(); + auto vals= obj["diceval"].toArray(); + int lastFace= -1; + for(auto const& valRef : qAsConst(vals)) + { + auto diceObj= valRef.toObject(); + auto face= diceObj["face"].toInt(); + auto it= std::find_if(std::begin(map), std::end(map), + [face](const std::pair<int, QList<QStringList>>& val) { return val.first == face; }); + + auto realVal= diceObj["string"].toString(); + if(lastFace == -1 || lastFace != face) + { + QList<QStringList> listOfList; + listOfList << (QStringList() << realVal); + map.push_back({face, listOfList}); + } + else if(lastFace == face) + { + auto& valList= it->second.last(); + valList.append(realVal); + } + lastFace= face; + } + instructionResult.push_back(map); + } + std::transform(std::begin(instructionResult), std::end(instructionResult), std::back_inserter(resultList), + [](const std::vector<std::pair<int, QList<QStringList>>>& map) + { + QStringList valuesStr; + auto multiKey= (map.size() > 1); + for(auto item : map) + { + auto face= item.first; + auto valueList= item.second; + QStringList strs; + for(auto list : valueList) + { + strs << list.join(","); + } + if(!multiKey) + valuesStr << strs.join(","); + else + valuesStr << QString("d%1:(%2)").arg(face).arg(strs.join(",")); + } + return valuesStr.join(" - "); + }); + + QString result= source; + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readPlaceHolderFromString(source, start); + if(ref.isValid()) + { + result.remove(ref.position(), ref.length()); + auto val= resultList[ref.resultIndex() - 1]; + result.insert(ref.position(), val); + } + else + { + valid= false; + } + } while(valid); + + return result; +} + +QString ParsingToolBox::replacePlaceHolderToValue(const QString& source, const QList<ExportedDiceResult>& list, + bool removeUnhighlighted, + std::function<QString(const QString&, const QString&, bool)> colorize) +{ + QStringList resultList; + std::transform( + std::begin(list), std::end(list), std::back_inserter(resultList), + [removeUnhighlighted, colorize](const ExportedDiceResult& dice) + { + QStringList valuesStr; + if(dice.size() == 1) + { + auto values= dice.values(); + std::transform( + std::begin(values), std::end(values), std::back_inserter(valuesStr), + [removeUnhighlighted, colorize](const QList<ListDiceResult>& dice) + { + QStringList textList; + std::transform( + std::begin(dice), std::end(dice), std::back_inserter(textList), + [removeUnhighlighted, colorize](const ListDiceResult& dice) + { + QStringList list; + ListDiceResult values= dice; + if(removeUnhighlighted) + { + values.clear(); + std::copy_if(std::begin(dice), std::end(dice), std::back_inserter(values), + [](const HighLightDice& hl) { return hl.isHighlighted(); }); + } + + std::transform(std::begin(values), std::end(values), std::back_inserter(list), + [colorize](const HighLightDice& hl) + { return colorize(hl.getResultString(), {}, hl.isHighlighted()); }); + return list.join(","); + }); + textList.removeAll(QString()); + return textList.join(","); + }); + } + else if(dice.size() > 1) + { + for(auto key : dice.keys()) + { + auto list= dice.value(key); + for(auto values : list) + { + QStringList textVals; + std::transform(std::begin(values), std::end(values), std::back_inserter(textVals), + [](const HighLightDice& dice) { return dice.getResultString(); }); + valuesStr.append(QString("d%1 [%2]").arg(key).arg(textVals.join(","))); + } + } + } + return valuesStr.join(","); + }); + + QString result= source; + int start= source.size() - 1; + bool valid= true; + do + { + auto ref= readPlaceHolderFromString(source, start); + if(ref.isValid()) + { + result.remove(ref.position(), ref.length()); + auto val= resultList[ref.resultIndex() - 1]; + result.insert(ref.position(), val); + } + else + { + valid= false; + } + } while(valid); + + return result; +} +void ParsingToolBox::readSubtitutionParameters(SubtituteInfo& info, QString& rest) +{ + auto sizeS= rest.size(); + if(rest.startsWith("{")) + { + rest= rest.remove(0, 1); + qint64 number; + if(readNumber(rest, number)) + { + if(rest.startsWith("}")) + { + rest= rest.remove(0, 1); + info.setDigitNumber(static_cast<int>(number)); + } + } + } + if(rest.startsWith("[")) + { + rest= rest.remove(0, 1); + qint64 number; + if(readNumber(rest, number)) + { + if(rest.startsWith("]")) + { + rest= rest.remove(0, 1); + info.setSubIndex(static_cast<int>(number)); + } + } + } + info.setLength(info.length() + sizeS - rest.size()); +} + +bool ParsingToolBox::readReaperArguments(RepeaterNode* node, QString& source) +{ + if(!readOpenParentheses(source)) + return false; + + auto instructions= readInstructionList(source, false); + if(instructions.empty()) + return false; + + readComma(source); + ExecutionNode* tmp; + if(readOperand(source, tmp)) + { + if(source.startsWith("+")) + { + node->setSumAll(true); + source= source.remove(0, 1); + } + if(readCloseParentheses(source)) + { + node->setCommand(instructions); + node->setTimeNode(tmp); + return true; + } + } + + return false; +} +bool ParsingToolBox::readExpression(QString& str, ExecutionNode*& node) +{ + ExecutionNode* operandNode= nullptr; + if(readOpenParentheses(str)) + { + ExecutionNode* internalNode= nullptr; + if(readExpression(str, internalNode)) + { + ParenthesesNode* parentheseNode= new ParenthesesNode(); + parentheseNode->setInternelNode(internalNode); + node= parentheseNode; + if(readCloseParentheses(str)) + { + ExecutionNode* diceNode= nullptr; + ExecutionNode* operatorNode= nullptr; + if(readDice(str, diceNode)) + { + parentheseNode->setNextNode(diceNode); + } + else if(readExpression(str, operatorNode)) + { + parentheseNode->setNextNode(operatorNode); + } + return true; + } + else + { + m_warningMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Expected closing parenthesis - can't validate the inside.")); + } + } + } + else if(readFunction(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOptionFromNull(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOperatorFromNull(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readOperand(str, operandNode)) + { + ExecutionNode* diceNode= nullptr; + if(readDice(str, diceNode)) + { + operandNode->setNextNode(diceNode); + } + node= operandNode; + + operandNode= ParsingToolBox::getLeafNode(operandNode); + // ExecutionNode* operatorNode=nullptr; + while(readOperator(str, operandNode)) + { + // operandNode->setNextNode(operatorNode); + operandNode= ParsingToolBox::getLeafNode(operandNode); + } + return true; + } + else if(readCommand(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readNode(str, operandNode)) + { + node= operandNode; + return true; + } + else if(readValuesList(str, operandNode)) + { + node= operandNode; + return true; + } + else + { + ExecutionNode* diceNode= nullptr; + if(readDice(str, diceNode)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(1); + numberNode->setNextNode(diceNode); + node= numberNode; + return true; + } + else + { + return false; + } + } + return false; +} + +bool ParsingToolBox::readValuesList(QString& str, ExecutionNode*& node) +{ + if(!str.startsWith("[")) + return false; + + str= str.remove(0, 1); + int pos= ParsingToolBox::findClosingCharacterIndexOf('[', ']', str, 1); // str.indexOf("]"); + if(-1 == pos) + return false; + + QString liststr= str.left(pos); + auto list= liststr.split(","); + str= str.remove(0, pos + 1); + auto values= new ValuesListNode(); + for(auto var : qAsConst(list)) + { + qint64 number= 1; + var= var.trimmed(); + if(ParsingToolBox::readDynamicVariable(var, number)) + { + VariableNode* variableNode= new VariableNode(); + variableNode->setIndex(static_cast<quint64>(number - 1)); + variableNode->setData(&m_startNodes); + values->insertValue(variableNode); + } + else if(ParsingToolBox::readNumber(var, number)) + { + NumberNode* numberNode= new NumberNode(); + numberNode->setNumber(number); + values->insertValue(numberNode); + } + } + node= values; + return true; +} +bool ParsingToolBox::readOptionFromNull(QString& str, ExecutionNode*& node) +{ + StartingNode nodePrevious; + if(readOption(str, &nodePrevious)) + { + auto nodeNext= nodePrevious.getNextNode(); + nodePrevious.setNextNode(nullptr); + node= nodeNext; + return true; + } + return false; +} + +void ParsingToolBox::setHelpPath(const QString& path) +{ + m_helpPath= path; +} + +bool ParsingToolBox::readOperatorFromNull(QString& str, ExecutionNode*& node) +{ + StartingNode nodePrevious; + if(readOperator(str, &nodePrevious)) + { + auto nodeNext= nodePrevious.getNextNode(); + nodePrevious.setNextNode(nullptr); + node= nodeNext; + return true; + } + return false; +} + +bool ParsingToolBox::readOption(QString& str, ExecutionNode* previous) //, +{ + if(str.isEmpty()) + { + return false; + } + + bool found= false; + auto keys= m_OptionOp.keys(); + for(int i= 0; ((i < keys.size()) && (!found)); ++i) + { + QString key= keys.at(i); + + if(str.startsWith(key)) + { + str= str.remove(0, key.size()); + auto operatorName= m_OptionOp.value(key); + switch(operatorName) + { + case Keep: + { + ExecutionNode* value= nullptr; + bool ascending= readAscending(str); + + if(readOperand(str, value)) + { + auto node= addSort(previous, ascending); + KeepDiceExecNode* nodeK= new KeepDiceExecNode(); + nodeK->setDiceKeepNumber(value); + node->setNextNode(nodeK); + found= true; + } + } + break; + case KeepAndExplode: + { + bool ascending= readAscending(str); + ExecutionNode* value= nullptr; + if(readOperand(str, value)) + { + DiceRollerNode* nodeTmp= dynamic_cast<DiceRollerNode*>(previous); + if(nullptr != nodeTmp) + { + previous= addExplodeDiceNode(static_cast<qint64>(nodeTmp->getFaces()), previous); + } + + auto node= addSort(previous, ascending); + + KeepDiceExecNode* nodeK= new KeepDiceExecNode(); + nodeK->setDiceKeepNumber(value); + + node->setNextNode(nodeK); + node= nodeK; + found= true; + } + } + break; + case Filter: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + auto validity= isValidValidator(previous, validatorList); + + FilterNode* filterNode= new FilterNode(); + filterNode->setValidatorList(validatorList); + + previous->setNextNode(filterNode); + found= true; + } + } + break; + case Sort: + { + bool ascending= readAscending(str); + addSort(previous, ascending); + /*if(!hasDice) + { + m_errorMap.insert(ExecutionNode::BAD_SYNTAXE,QObject::tr("Sort Operator does not support default + dice. You should add dice command before the s")); + }*/ + found= true; + } + break; + case Count: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + auto validity= isValidValidator(previous, validatorList); + + CountExecuteNode* countNode= new CountExecuteNode(); + countNode->setValidatorList(validatorList); + + previous->setNextNode(countNode); + found= true; + } + else + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the c operator. Please, change it")); + } + } + break; + case Reroll: + case RerollUntil: + case RerollAndAdd: + // Todo: I think that Exploding and Rerolling could share the same code + { + auto validatorList= readValidatorList(str); + QString symbol= m_OptionOp.key(operatorName); + if(nullptr != validatorList) + { + switch(isValidValidator(previous, validatorList)) + { + case Dice::CONDITION_STATE::ALWAYSTRUE: + if(operatorName == RerollAndAdd) + { + m_errorMap.insert( + Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Validator is always true for the %1 operator. Please, change it") + .arg(symbol)); + } + break; + case Dice::CONDITION_STATE::UNREACHABLE: + if(operatorName == RerollUntil) + { + m_errorMap.insert( + Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("Condition can't be reached, causing endless loop. Please, " + "change the %1 option condition") + .arg(symbol)); + } + break; + case Dice::CONDITION_STATE::ERROR_STATE: + default: + break; + } + + auto reroll= (operatorName == RerollAndAdd || operatorName == Reroll); + auto addingMode= (operatorName == RerollAndAdd); + RerollDiceNode* rerollNode= new RerollDiceNode(reroll, addingMode); + ExecutionNode* nodeParam= nullptr; + if(readParameterNode(str, nodeParam)) + { + rerollNode->setInstruction(nodeParam); + } + rerollNode->setValidatorList(validatorList); + previous->setNextNode(rerollNode); + found= true; + } + else + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the %1 operator. Please, change it").arg(symbol)); + } + } + break; + case Explode: + { + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + if(Dice::CONDITION_STATE::ALWAYSTRUE == isValidValidator(previous, validatorList)) + { + m_errorMap.insert(Dice::ERROR_CODE::ENDLESS_LOOP_ERROR, + QObject::tr("This condition %1 introduces an endless loop. Please, change it") + .arg(validatorList->toString())); + } + ExplodeDiceNode* explodedNode= new ExplodeDiceNode(); + explodedNode->setValidatorList(validatorList); + previous->setNextNode(explodedNode); + found= true; + } + else + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Validator is missing after the e operator. Please, change it")); + } + } + break; + case Merge: + { + MergeNode* mergeNode= new MergeNode(); + mergeNode->setStartList(&m_startNodes); + previous->setNextNode(mergeNode); + found= true; + } + break; + case AllSameExplode: + { + AllSameNode* allSame= new AllSameNode(); + previous->setNextNode(allSame); + found= true; + } + break; + case SwitchCaseOption: + { + auto scNode= new SwitchCaseNode(); + found= readSwitchCaseNode(str, scNode); + previous->setNextNode(scNode); + } + break; + case TransformOption: + { + auto scNode= new ReplaceValueNode(); + found= readReplaceValueNode(str, scNode); + previous->setNextNode(scNode); + } + break; + case Bind: + { + BindNode* bindNode= new BindNode(); + bindNode->setStartList(&m_startNodes); + previous->setNextNode(bindNode); + found= true; + } + break; + case Occurences: + { + qint64 number= 0; + auto occNode= new OccurenceCountNode(); + if(readNumber(str, number)) + { + occNode->setWidth(number); + auto validatorList= readValidatorList(str); + if(validatorList) + { + occNode->setValidatorList(validatorList); + } + else if(readComma(str)) + { + if(readNumber(str, number)) + { + occNode->setHeight(number); + } + } + } + previous->setNextNode(occNode); + found= true; + } + break; + case Unique: + { + auto node= new UniqueNode(); + previous->setNextNode(node); + found= true; + } + break; + case Painter: + { + PainterNode* painter= new PainterNode(); + if(!readPainterParameter(painter, str)) + { + m_errorMap.insert(Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Missing parameter for Painter node (p)")); + delete painter; + } + else + { + previous->setNextNode(painter); + found= true; + } + } + break; + case ifOperator: + { + IfNode* nodeif= new IfNode(); + nodeif->setConditionType(readConditionType(str)); + auto validatorList= readValidatorList(str); + if(nullptr != validatorList) + { + ExecutionNode* trueNode= nullptr; + ExecutionNode* falseNode= nullptr; + if(readIfInstruction(str, trueNode, falseNode)) + { + nodeif->setInstructionTrue(trueNode); + nodeif->setInstructionFalse(falseNode); + nodeif->setValidatorList(validatorList); + previous->setNextNode(nodeif); + found= true; + } + else + { + delete nodeif; + } + } + else + { + delete nodeif; + } + break; + } + case Split: + { + SplitNode* splitnode= new SplitNode(); + previous->setNextNode(splitnode); + found= true; + } + break; + case Group: + { + bool stringResult= readStringResultParameter(str); + qint64 groupNumber= 0; + if(readNumber(str, groupNumber)) + { + GroupNode* groupNode= new GroupNode(stringResult); + groupNode->setGroupValue(groupNumber); + previous->setNextNode(groupNode); + found= true; + } + } + break; + } + } + } + return found; +} +bool ParsingToolBox::readStringResultParameter(QString& str) +{ + if(str.startsWith("s")) + { + str.remove(0, 1); + return true; + } + return false; +} +bool ParsingToolBox::readIfInstruction(QString& str, ExecutionNode*& trueNode, ExecutionNode*& falseNode) +{ + if(readBlocInstruction(str, trueNode)) + { + if(readBlocInstruction(str, falseNode)) + { + return true; + } + return true; + } + return false; +} +DiceRollerNode* ParsingToolBox::addRollDiceNode(qint64 faces, ExecutionNode* previous) +{ + DiceRollerNode* mydiceRoller= new DiceRollerNode(faces); + previous->setNextNode(mydiceRoller); + return mydiceRoller; +} +ExplodeDiceNode* ParsingToolBox::addExplodeDiceNode(qint64 value, ExecutionNode* previous) +{ + ExplodeDiceNode* explodeDiceNode= new ExplodeDiceNode(); + NumberNode* node= new NumberNode(); + node->setNumber(value); + BooleanCondition* condition= new BooleanCondition(); + condition->setConditionType(Dice::OnEach); + condition->setValueNode(node); + condition->setOperator(Dice::CompareOperator::Equal); + auto valList= new ValidatorList(); + valList->setValidators(QList<Validator*>() << condition); + auto validity= isValidValidator(previous, valList); + explodeDiceNode->setValidatorList(valList); + previous->setNextNode(explodeDiceNode); + return explodeDiceNode; +} +bool ParsingToolBox::readParameterNode(QString& str, ExecutionNode*& node) +{ + if(str.startsWith("(")) + { + str= str.remove(0, 1); + if(readExpression(str, node)) + { + if(str.startsWith(")")) + { + str= str.remove(0, 1); + return true; + } + } + } + return false; +} + +bool ParsingToolBox::readSwitchCaseNode(QString& str, SwitchCaseNode* node) +{ + bool res= false; + node->setStopAtFirt(ParsingToolBox::readStopAtFirst(str)); + bool hasInstructionBloc= false; + do + { + auto validator= readValidatorList(str); + ExecutionNode* exeNode= nullptr; + hasInstructionBloc= readBlocInstruction(str, exeNode); + if(hasInstructionBloc) + { + node->insertCase(exeNode, validator); + res= true; + } + + } while(hasInstructionBloc); + + return res; +} + +bool ParsingToolBox::readReplaceValueNode(QString& str, ReplaceValueNode* node) +{ + bool res= false; + bool hasInstructionBloc= false; + do + { + auto validator= readValidatorList(str); + ExecutionNode* exeNode= nullptr; + hasInstructionBloc= readBlocInstruction(str, exeNode); + if(hasInstructionBloc) + { + node->insertCase(exeNode, validator); + res= true; + } + + } while(hasInstructionBloc); + + return res; +} + +bool ParsingToolBox::readBlocInstruction(QString& str, ExecutionNode*& resultnode) +{ + if(str.startsWith('{')) + { + str= str.remove(0, 1); + ExecutionNode* node= nullptr; + Dice::ArithmeticOperator op; + ScalarOperatorNode* scalarNode= nullptr; + if(readArithmeticOperator(str, op)) + { + scalarNode= new ScalarOperatorNode(); + scalarNode->setArithmeticOperator(op); + } + if(readExpression(str, node)) + { + if(str.startsWith('}')) + { + if(nullptr == scalarNode) + { + resultnode= node; + } + else + { + resultnode= scalarNode; + scalarNode->setInternalNode(node); + } + str= str.remove(0, 1); + return true; + } + } + } + return false; +} +bool ParsingToolBox::readDice(QString& str, ExecutionNode*& node) +{ + DiceOperator currentOperator; + + if(readDiceOperator(str, currentOperator)) + { + if(currentOperator == D) + { + qint64 max; + qint64 min; + bool unique= (ParsingToolBox::UNIQUE == readListOperator(str)) ? true : false; + Dice::ArithmeticOperator op; + + bool hasOp= readArithmeticOperator(str, op); + if(readNumber(str, max)) + { + if(max < 1) + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr("Dice with %1 face(s) does not exist. Please, put a value higher than 0").arg(max)); + return false; + } + DiceRollerNode* drNode= new DiceRollerNode(max); + drNode->setUnique(unique); + if(hasOp) + { + drNode->setOperator(op); + } + node= drNode; + ExecutionNode* current= drNode; + while(readOption(str, current)) + { + current= ParsingToolBox::getLeafNode(current); + } + return true; + } + else if(readDiceRange(str, min, max)) + { + DiceRollerNode* drNode= new DiceRollerNode(max, min); + drNode->setUnique(unique); + if(hasOp) + { + drNode->setOperator(op); + } + node= drNode; + ExecutionNode* current= drNode; + while(readOption(str, current)) + { + current= ParsingToolBox::getLeafNode(current); + } + return true; + } + } + else if(currentOperator == L) + { + QStringList list; + QList<Range> listRange; + ParsingToolBox::LIST_OPERATOR op= readListOperator(str); + if(readList(str, list, listRange)) + { + ListSetRollNode* lsrNode= new ListSetRollNode(); + lsrNode->setRangeList(listRange); + if(op == ParsingToolBox::UNIQUE || op == ParsingToolBox::UniqueAndNoComma) + { + lsrNode->setUnique(true); + } + if(op == ParsingToolBox::NOCOMMA || op == ParsingToolBox::UniqueAndNoComma) + { + lsrNode->setNoComma(true); + } + lsrNode->setListValue(list); + node= lsrNode; + return true; + } + else + { + m_errorMap.insert( + Dice::ERROR_CODE::BAD_SYNTAXE, + QObject::tr( + "List is missing after the L operator. Please, add it (e.g : 1L[sword,spear,gun,arrow])")); + } + } + } + + return false; +} +bool ParsingToolBox::readDiceOperator(QString& str, DiceOperator& op) +{ + QStringList listKey= m_mapDiceOp.keys(); + for(const QString& key : qAsConst(listKey)) + { + if(str.startsWith(key, Qt::CaseInsensitive)) + { + str= str.remove(0, key.size()); + op= m_mapDiceOp.value(key); + return true; + } + } + return false; +} +QString ParsingToolBox::convertAlias(QString str) +{ + for(auto& cmd : m_aliasList) + { + if(cmd->isEnable()) + { + cmd->resolved(str); + } + } + return str; +} + +bool ParsingToolBox::readCommand(QString& str, ExecutionNode*& node) +{ + if(m_commandList.contains(str)) + { + if(str == QLatin1String("help")) + { + str= str.remove(0, QLatin1String("help").size()); + HelpNode* help= new HelpNode(); + if(!m_helpPath.isEmpty()) + { + help->setHelpPath(m_helpPath); + } + node= help; + } + else if(str == QLatin1String("la")) + { + str= str.remove(0, QLatin1String("la").size()); + node= new ListAliasNode(m_aliasList); + } + return true; + } + return false; +} + +bool ParsingToolBox::readDiceExpression(QString& str, ExecutionNode*& node) +{ + bool returnVal= false; + + ExecutionNode* next= nullptr; + if(readDice(str, next)) + { + ExecutionNode* latest= next; + while(readOption(str, latest)) + { + while(nullptr != latest->getNextNode()) + { + latest= latest->getNextNode(); + } + } + + node= next; + returnVal= true; + } + else + { + returnVal= false; + } + return returnVal; +} + +bool ParsingToolBox::readOperator(QString& str, ExecutionNode* previous) +{ + bool result= false; + if(str.isEmpty() || nullptr == previous) + { + return result; + } + + Dice::ArithmeticOperator op; + if(readArithmeticOperator(str, op)) + { + ScalarOperatorNode* node= new ScalarOperatorNode(); + node->setArithmeticOperator(op); + ExecutionNode* nodeExec= nullptr; + if(readExpression(str, nodeExec)) + { + node->setInternalNode(nodeExec); + if(nullptr == nodeExec) + { + delete node; + return result; + } + ExecutionNode* nodeExecOrChild= nodeExec; + ExecutionNode* parent= nullptr; + + while((nullptr != nodeExecOrChild) && (node->getPriority() < nodeExecOrChild->getPriority())) + { + parent= nodeExecOrChild; + nodeExecOrChild= nodeExecOrChild->getNextNode(); + } + // management of operator priority + if((nullptr != nodeExecOrChild) && (nodeExec != nodeExecOrChild)) + { + // good 1 1 2 ; bad 1 0 4 + if(nodeExecOrChild->getPriority() >= node->getPriority()) + { + node->setNextNode(nodeExecOrChild); + parent->setNextNode(nullptr); + } + } + else if(node->getPriority() >= nodeExec->getPriority()) + { + node->setNextNode(nodeExec->getNextNode()); + nodeExec->setNextNode(nullptr); + } + + // nodeResult = node; + previous->setNextNode(node); + + result= true; + } + else + { + delete node; + } + } + else + { + while(readOption(str, previous)) + { + previous= ParsingToolBox::getLeafNode(previous); + result= true; + } + } + return result; +} +bool ParsingToolBox::readFunction(QString& str, ExecutionNode*& node) +{ + for(const auto& kv : m_functionMap) + { + if(str.startsWith(kv.first)) + { + str= str.remove(0, kv.first.size()); + switch(kv.second) + { + case REPEAT: + { + auto repeaterNode= new RepeaterNode(); + if(ParsingToolBox::readReaperArguments(repeaterNode, str)) + { + node= repeaterNode; + } + } + break; + } + } + } + + if(node == nullptr) + return false; + return true; +} + +bool ParsingToolBox::readNode(QString& str, ExecutionNode*& node) +{ + if(str.isEmpty()) + return false; + + QString key= str.at(0); + if(m_nodeActionMap.contains(key)) + { + JumpBackwardNode* jumpNode= new JumpBackwardNode(); + node= jumpNode; + str= str.remove(0, 1); + readOption(str, jumpNode); + return true; + } + return false; +} + +bool ParsingToolBox::readInstructionOperator(QChar c) +{ + if(c == ';') + { + return true; + } + return false; +} + +void ParsingToolBox::insertAlias(DiceAlias* dice, int i) +{ + if(i >= m_aliasList.size()) + { + m_aliasList.insert(i, dice); + } +} +const QList<DiceAlias*>& ParsingToolBox::getAliases() const +{ + return m_aliasList; +} + +QList<DiceAlias*>* ParsingToolBox::aliases() +{ + return &m_aliasList; +} + +void ParsingToolBox::setAliases(const QList<DiceAlias*> list) +{ + qDeleteAll(m_aliasList); + m_aliasList.clear(); + m_aliasList= list; +} + +std::vector<ExecutionNode*> ParsingToolBox::readInstructionList(QString& str, bool global) +{ + if(str.isEmpty()) + return {}; + + std::vector<ExecutionNode*> startNodes; + + bool hasInstruction= false; + bool readInstruction= true; + while(readInstruction) + { + ExecutionNode* startNode= nullptr; + bool keepParsing= readExpression(str, startNode); + if(nullptr != startNode) + { + hasInstruction= true; + startNodes.push_back(startNode); + auto latest= startNode; + if(keepParsing) + { + latest= ParsingToolBox::getLeafNode(latest); + keepParsing= !str.isEmpty(); + while(keepParsing) + { + auto before= str; + if(readOperator(str, latest)) + { + latest= ParsingToolBox::getLeafNode(latest); + } + keepParsing= (!str.isEmpty() && (before != str)); + } + } + if(!str.isEmpty() && readInstructionOperator(str[0])) + { + str= str.remove(0, 1); + } + else + { + QString result; + QString comment; + if(readComment(str, result, comment)) + { + m_comment= result; + } + readInstruction= false; + } + } + else + { + readInstruction= false; + } + } + if(global) + m_startNodes= startNodes; + return startNodes; +} + +SubtituteInfo ParsingToolBox::readVariableFromString(const QString& source, int& start) +{ + bool found= false; + SubtituteInfo info; + int i= start; + for(; i >= 0 && !found; --i) + { + if(source.at(i) == '$') + { + auto rest= source.mid(i + 1, 1 + start - i); + qint64 number; + if(readNumber(rest, number)) + { + auto len= QString::number(number).size() - 1; + readSubtitutionParameters(info, rest); + info.setLength(info.length() + len); + info.setResultIndex(static_cast<int>(number)); + info.setPosition(i); + found= true; + } + } + } + start= i; + return info; +} + +SubtituteInfo ParsingToolBox::readPlaceHolderFromString(const QString& source, int& start) +{ + bool found= false; + SubtituteInfo info; + int i= start; + for(; i >= 0 && !found; --i) + { + if(source.at(i) == '@') + { + auto rest= source.mid(i + 1, 1 + start - i); + qint64 number; + if(readNumber(rest, number)) + { + auto len= QString::number(number).size() - 1; + readSubtitutionParameters(info, rest); + info.setLength(info.length() + len); + info.setResultIndex(static_cast<int>(number)); + info.setPosition(i); + found= true; + } + } + } + start= i; + return info; +} + +QString ParsingToolBox::number(qreal value) +{ + if(value > 1000000) + return QString::number(value, 'f', 20); + else + return QString::number(value); +} + +ExportedDiceResult ParsingToolBox::finalDiceResultFromInstruction(ExecutionNode* start) +{ + ExecutionNode* next= ParsingToolBox::getLeafNode(start); + Result* result= next->getResult(); + ExportedDiceResult nodeResult; + std::set<QString> alreadyAdded; + while(nullptr != result) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* diceResult= dynamic_cast<DiceResult*>(result); + QList<HighLightDice> list; + quint64 faces= 0; + for(auto& die : diceResult->getResultList()) + { + faces= die->getFaces(); + HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(), + die->hasBeenDisplayed(), die->getFaces(), die->getUuid()); + if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end() && !hlDice.displayed()) + { + list.append(hlDice); + alreadyAdded.insert(die->getUuid()); + } + } + if(!list.isEmpty()) + { + auto vals= nodeResult.value(faces); + vals.append(list); + nodeResult.insert(faces, vals); + } + } + /*if(nodeResult.isEmpty()) + result= result->getPrevious(); + else*/ + result= result->getPrevious(); + } + return nodeResult; +} + +ExportedDiceResult ParsingToolBox::allDiceResultFromInstruction(ExecutionNode* start) +{ + ExecutionNode* next= ParsingToolBox::getLeafNode(start); + Result* result= next->getResult(); + ExportedDiceResult nodeResult; + std::set<QString> alreadyAdded; + while(nullptr != result) + { + if(result->hasResultOfType(Dice::RESULT_TYPE::DICE_LIST)) + { + DiceResult* diceResult= dynamic_cast<DiceResult*>(result); + QList<HighLightDice> list; + quint64 faces= 0; + for(auto& die : diceResult->getResultList()) + { + faces= die->getFaces(); + HighLightDice hlDice(die->getListValue(), die->isHighlighted(), die->getColor(), + die->hasBeenDisplayed(), die->getFaces(), die->getUuid()); + if(alreadyAdded.find(die->getUuid()) == alreadyAdded.end()) + { + list.append(hlDice); + alreadyAdded.insert(die->getUuid()); + } + } + if(!list.isEmpty()) + { + auto vals= nodeResult.value(faces); + vals.append(list); + nodeResult.insert(faces, vals); + } + } + result= result->getPrevious(); + } + return nodeResult; +} + +void ParsingToolBox::addResultInJson(QJsonObject& obj, Dice::RESULT_TYPE type, const QString& key, ExecutionNode* start, + bool b) +{ + auto pair= hasResultOfType(type, start, b); + if(pair.first) + obj[key]= QJsonValue::fromVariant(pair.second); +} + +void ParsingToolBox::addDiceResultInJson( + QJsonObject& obj, ExecutionNode* start, + std::function<QString(const QString& value, const QString& color, bool highlighted)> colorize) +{ + QJsonArray diceValues; + auto result= ParsingToolBox::allDiceResultFromInstruction(start); + for(auto listOfList : result.values()) + { + for(auto listDiceResult : listOfList) + { + for(auto hlDice : listDiceResult) + { + QJsonObject diceObj; + diceObj["face"]= static_cast<qreal>(hlDice.faces()); + diceObj["color"]= hlDice.color(); + diceObj["displayed"]= hlDice.displayed(); + diceObj["string"]= colorize(hlDice.getResultString(), hlDice.color(), hlDice.isHighlighted()); + diceObj["highlight"]= hlDice.isHighlighted(); + diceObj["uuid"]= hlDice.uuid(); + auto val= hlDice.result(); + if(!val.isEmpty()) + { + diceObj["value"]= std::accumulate(val.begin(), val.end(), 0); + if(val.size() > 1) + { + QJsonArray intValues; + std::transform(val.begin(), val.end(), std::back_inserter(intValues), + [](qint64 val) { return static_cast<int>(val); }); + diceObj["subvalues"]= intValues; + } + } + diceValues.append(diceObj); + } + } + } + if(!diceValues.isEmpty()) + obj["diceval"]= diceValues; +} + +SubtituteInfo::SubtituteInfo() {} + +bool SubtituteInfo::isValid() const +{ + return !(m_position + m_resultIndex < 0); +} + +int SubtituteInfo::length() const +{ + return m_length; +} + +void SubtituteInfo::setLength(int length) +{ + m_length= length; +} + +int SubtituteInfo::resultIndex() const +{ + return m_resultIndex; +} + +void SubtituteInfo::setResultIndex(int valueIndex) +{ + m_resultIndex= valueIndex; +} + +int SubtituteInfo::position() const +{ + return m_position; +} + +void SubtituteInfo::setPosition(int position) +{ + m_position= position; +} + +int SubtituteInfo::digitNumber() const +{ + return m_digitNumber; +} + +void SubtituteInfo::setDigitNumber(int digitNumber) +{ + m_digitNumber= digitNumber; +} + +int SubtituteInfo::subIndex() const +{ + return m_subIndex; +} + +void SubtituteInfo::setSubIndex(int subindex) +{ + m_subIndex= subindex; +} |