File indexing completed on 2024-05-12 05:39:26
0001 /*************************************************************************** 0002 * Copyright (C) 2014 by Renaud Guezennec * 0003 * http://www.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 0023 #include <QCommandLineOption> 0024 #include <QCommandLineParser> 0025 #include <QDebug> 0026 #include <QFile> 0027 #include <QJsonArray> 0028 #include <QJsonDocument> 0029 #include <QJsonObject> 0030 #include <QRegularExpression> 0031 #include <QSettings> 0032 #include <QStringList> 0033 #include <QTextStream> 0034 #include <set> 0035 0036 #ifdef PAINTER_OP 0037 #include <QGuiApplication> 0038 #else 0039 #include <QCoreApplication> 0040 #endif 0041 0042 #include "diceparser/dicealias.h" 0043 #include "diceparser/diceparser.h" 0044 #include "diceparser/highlightdice.h" 0045 #include "diceparser/parsingtoolbox.h" 0046 #include "displaytoolbox.h" 0047 0048 /** 0049 * @page Dice 0050 * The cli for DiceParser the new dice system from rolisteam. 0051 * @section Build and install 0052 * To build this program, type these command: 0053 * - mkdir build 0054 * - cd build 0055 * - cmake ../ 0056 * - make 0057 * - make install 0058 * @return 0059 */ 0060 0061 QTextStream out(stdout, QIODevice::WriteOnly); 0062 QTextStream err(stderr, QIODevice::WriteOnly); 0063 bool markdown= false; 0064 constexpr char const* colorkey= {"dicecolor"}; 0065 #ifdef PAINTER_OP 0066 enum EXPORTFORMAT 0067 { 0068 TERMINAL, 0069 SVG, 0070 IMAGE, 0071 MARKDOWN, 0072 JSON, 0073 BOT, 0074 TEXT 0075 }; 0076 #else 0077 enum EXPORTFORMAT 0078 { 0079 TERMINAL, 0080 SVG, 0081 MARKDOWN, 0082 JSON, 0083 BOT, 0084 TEXT 0085 }; 0086 #endif 0087 int returnValue= 0; 0088 0089 void displayJSon(QString json) 0090 { 0091 out << json << "\n"; 0092 } 0093 void displayMarkdown(QString json) 0094 { 0095 QJsonDocument doc= QJsonDocument::fromJson(json.toUtf8()); 0096 auto obj= doc.object(); 0097 auto error= obj["error"].toString(); 0098 auto warning= obj["warning"].toString(); 0099 auto comment= obj["comment"].toString(); 0100 auto arrayInst= obj["instructions"].toArray(); 0101 QStringList diceResults; 0102 for(auto inst : arrayInst) 0103 { 0104 auto obj= inst.toObject(); 0105 auto diceVals= obj["diceval"].toArray(); 0106 for(auto diceval : diceVals) 0107 { 0108 auto objval= diceval.toObject(); 0109 auto resultStr= QString::number(objval["value"].toDouble()); 0110 0111 auto subvalues= objval["subvalues"].toArray(); 0112 QStringList subValueStr; 0113 for(auto sub : subvalues) 0114 { 0115 subValueStr << QString::number(sub.toDouble()); 0116 } 0117 diceResults << resultStr; 0118 if(!subValueStr.isEmpty()) 0119 diceResults << QString("[%1]").arg(subValueStr.join(" ")); 0120 } 0121 } 0122 auto diceList= diceResults.join(" "); 0123 auto resultStr= obj["string"].toString(); 0124 auto scalarText= obj["scalar"].toString(); 0125 auto cmd= obj["command"].toString(); 0126 0127 QString str("```Markdown\n"); 0128 if(!error.isEmpty()) 0129 { 0130 str.append(QObject::tr("Error: %1\n").arg(error)); 0131 } 0132 else 0133 { 0134 if(!warning.isEmpty()) 0135 str.append(QObject::tr("Warning: %1\n").arg(warning)); 0136 0137 if(!comment.isEmpty()) 0138 { 0139 str.prepend(QStringLiteral("%1\n").arg(comment)); 0140 } 0141 if(!resultStr.isEmpty() && resultStr != scalarText) 0142 { 0143 // resultStr.replace("%2", diceList.trimmed()); 0144 str.append(QStringLiteral("%1\n").arg(resultStr)); 0145 } 0146 else 0147 { 0148 str.append(QObject::tr("# %1\nDetails:[%3 (%2)]\n").arg(scalarText).arg(diceList).arg(cmd)); 0149 } 0150 } 0151 str.append(QStringLiteral("```")); 0152 out << str; 0153 } 0154 QString displaySVG(QString json, bool withColor) 0155 { 0156 QJsonDocument doc= QJsonDocument::fromJson(json.toUtf8()); 0157 auto obj= doc.object(); 0158 auto error= obj["error"].toString(); 0159 auto warning= obj["warning"].toString(); 0160 auto comment= obj["warning"].toString(); 0161 auto arrayInst= obj["instructions"].toArray(); 0162 QStringList diceResults; 0163 for(auto inst : arrayInst) 0164 { 0165 auto obj= inst.toObject(); 0166 auto diceVals= obj["diceval"].toArray(); 0167 for(auto diceval : diceVals) 0168 { 0169 auto objval= diceval.toObject(); 0170 auto resultStr= objval["string"].toString(); 0171 diceResults << resultStr; 0172 } 0173 } 0174 auto diceList= diceResults.join(","); 0175 auto resultStr= obj["string"].toString(); 0176 auto scalarText= obj["scalar"].toString(); 0177 auto cmd= obj["command"].toString(); 0178 0179 QString str("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<svg version=\"1.1\" " 0180 "xmlns=\"http://www.w3.org/2000/svg\" " 0181 "xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"); 0182 if(!error.isEmpty()) 0183 { 0184 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"20\"><tspan " 0185 "fill=\"red\">%1</tspan></text>") 0186 .arg(error)); 0187 } 0188 else 0189 { 0190 if(!warning.isEmpty()) 0191 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"20\"><tspan " 0192 "fill=\"orange\">%1</tspan></text>") 0193 .arg(warning)); 0194 0195 int y= 20; 0196 if(!comment.isEmpty()) 0197 { 0198 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%2\"><tspan " 0199 "fill=\"blue\">%1</tspan></text>") 0200 .arg(comment) 0201 .arg(y)); 0202 y+= 20; 0203 } 0204 // auto diceList= DisplayToolBox::diceToSvg(array, withColor, allSameColor, allSameFaceCount); 0205 if(!resultStr.isEmpty() && resultStr != scalarText) 0206 { 0207 resultStr.replace("%2", diceList.trimmed()); 0208 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%2\">%1</text>").arg(resultStr).arg(y)); 0209 } 0210 else 0211 { 0212 if(withColor) 0213 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%4\"><tspan " 0214 "fill=\"red\">%1</tspan>\n" 0215 "<tspan x=\"0\" y=\"%5\">details:</tspan>[%3 (%2)]</text>") 0216 .arg(scalarText) 0217 .arg(diceList) 0218 .arg(cmd) 0219 .arg(y) 0220 .arg(y * 2)); 0221 else 0222 str.append(QStringLiteral("<text font-size=\"16\" x=\"0\" y=\"%4\"><tspan>%1</tspan>\n" 0223 "<tspan x=\"0\" y=\"%5\">details:</tspan>[%3 (%2)]</text>") 0224 .arg(scalarText) 0225 .arg(diceList) 0226 .arg(cmd) 0227 .arg(y) 0228 .arg(y * 2)); 0229 } 0230 } 0231 str.append(QStringLiteral("</svg>\n")); 0232 return str; 0233 } 0234 0235 #ifdef PAINTER_OP 0236 void displayImage(QString json, bool withColor) 0237 { 0238 auto svg= displaySVG(json, withColor); 0239 out << DisplayToolBox::makeImage(svg.toUtf8()); 0240 } 0241 #endif 0242 0243 void displayCommandResult(QString json, bool withColor, QString color) 0244 { 0245 QJsonDocument doc= QJsonDocument::fromJson(json.toUtf8()); 0246 auto obj= doc.object(); 0247 auto error= obj["error"].toString(); 0248 auto warning= obj["warning"].toString(); 0249 auto comment= obj["comment"].toString(); 0250 auto arrayInst= obj["instructions"].toArray(); 0251 QStringList diceResults; 0252 for(const auto& inst : qAsConst(arrayInst)) 0253 { 0254 auto obj= inst.toObject(); 0255 auto diceVals= obj["diceval"].toArray(); 0256 for(const auto& diceval : qAsConst(diceVals)) 0257 { 0258 auto objval= diceval.toObject(); 0259 auto resultStr= objval["string"].toString(); 0260 diceResults << resultStr; 0261 } 0262 } 0263 auto diceList= diceResults.join(","); 0264 auto scalarText= obj["scalar"].toString(); 0265 auto cmd= obj["command"].toString(); 0266 auto resultStr= obj["string"].toString(); 0267 0268 if(!error.isEmpty()) 0269 { 0270 err << "Error" << error << "\n"; 0271 return; 0272 } 0273 0274 if(!warning.isEmpty()) 0275 err << "Warning: " << warning << "\n"; 0276 0277 QString str; 0278 0279 if(withColor) 0280 str= QObject::tr("Result: \e[0;%4;1m%1\e[0m - details:[%3 (%2)]") 0281 .arg(scalarText, diceList, cmd, DisplayToolBox::colorToIntCode(color)); 0282 else 0283 str= QObject::tr("Result: %1 - details:[%3 (%2)]").arg(scalarText, diceList, cmd); 0284 0285 if(!resultStr.isEmpty() && resultStr != scalarText) 0286 { 0287 resultStr.replace("%2", diceList.trimmed()); 0288 str= resultStr; 0289 } 0290 0291 if(!comment.isEmpty()) 0292 { 0293 if(withColor) 0294 out << "\033[1m" << comment << "\033[0m\n"; 0295 else 0296 out << comment << " "; 0297 } 0298 out << str << "\n"; 0299 } 0300 0301 int startDiceParsing(QStringList& cmds, bool withColor, QString baseColor, EXPORTFORMAT format, QJsonArray array, 0302 const QString& filePath) 0303 { 0304 DiceParser parser; 0305 parser.insertAlias(new DiceAlias("L5R5R", QStringLiteral("L[-,⨀,⨀⬢,❂⬢,❁,❁⬢]")), 0); 0306 parser.insertAlias(new DiceAlias("L5R5S", QStringLiteral("L[-,-,⨀,⨀,⨀❁,⨀⬢,⨀⬢,❂,❂⬢,❁,❁,❁]")), 1); 0307 int i= 2; 0308 for(auto alias : array) 0309 { 0310 auto objAlias= alias.toObject(); 0311 parser.insertAlias(new DiceAlias(objAlias["pattern"].toString(), objAlias["cmd"].toString(), 0312 objAlias["comment"].toString(), !objAlias["regexp"].toBool()), 0313 i++); 0314 } 0315 0316 int rt= 0; 0317 bool in_markdown= true; 0318 0319 for(QString cmd : cmds) 0320 { 0321 if(cmd.startsWith('&') && format == BOT) 0322 { 0323 cmd= cmd.remove(0, 1); 0324 in_markdown= false; 0325 } 0326 0327 if(parser.parseLine(cmd)) 0328 { 0329 parser.start(); 0330 0331 std::set<EXPORTFORMAT> svgFormat({SVG, IMAGE, BOT}); 0332 QString json; 0333 bool allSameColor= true; 0334 if(svgFormat.find(format) != svgFormat.end()) 0335 { 0336 allSameColor= true; 0337 QString colorP; 0338 json= parser.resultAsJSon( 0339 [&colorP, &allSameColor](const QString& value, const QString& color, bool) { 0340 if(colorP.isNull()) 0341 colorP= color; 0342 else if(colorP != color) 0343 allSameColor= false; 0344 0345 return value; 0346 }, 0347 true); 0348 0349 if(!allSameColor) 0350 { 0351 json= parser.resultAsJSon([](const QString& value, const QString& color, bool highlight) { 0352 QString result= value; 0353 bool hasColor= !color.isEmpty(); 0354 QString style; 0355 if(hasColor) 0356 { 0357 style+= QStringLiteral("fill=\"%1\" ").arg(color); 0358 } 0359 if(highlight) 0360 { 0361 if(style.isEmpty()) 0362 style+= QStringLiteral("fill=\"%1\" ") 0363 .arg("red"); // default color must get the value from the setting object 0364 style+= QStringLiteral("font-weight=\"bold\" "); 0365 } 0366 if(!style.isEmpty()) 0367 result= QString("<tspan %2>%1</tspan>").arg(value).arg(style); 0368 return result; 0369 }); 0370 } 0371 } 0372 else if(TERMINAL == format) 0373 { 0374 allSameColor= true; 0375 QString colorP; 0376 json= parser.resultAsJSon( 0377 [&colorP, &allSameColor, &baseColor](const QString& result, const QString& color, bool hightlight) { 0378 auto trueColor= color; 0379 if(color.isEmpty()) 0380 trueColor= baseColor; 0381 0382 if(colorP.isEmpty()) 0383 colorP= trueColor; 0384 else if(colorP != trueColor) 0385 allSameColor= false; 0386 0387 auto front= DisplayToolBox::colorToTermCode(trueColor); 0388 auto end= front.isEmpty() ? "" : DisplayToolBox::colorToTermCode("reset"); 0389 return hightlight ? QString("%1%2%3").arg(front).arg(result).arg(end) : result; 0390 }); 0391 } 0392 else 0393 { 0394 allSameColor= true; 0395 json= parser.resultAsJSon([](const QString& result, const QString&, bool) { return result; }); 0396 } 0397 0398 if(format == BOT) 0399 { 0400 if(allSameColor) 0401 { 0402 format= in_markdown ? MARKDOWN : TEXT; 0403 } 0404 else 0405 { 0406 #ifdef PAINTER_OP 0407 format= IMAGE; 0408 #else 0409 format= MARKDOWN; 0410 #endif 0411 } 0412 if(!parser.humanReadableError().isEmpty()) 0413 { 0414 format= MARKDOWN; 0415 } 0416 } 0417 if(!withColor && format == TERMINAL) 0418 format= TEXT; 0419 0420 // qDebug().noquote() << json << format; 0421 0422 switch(format) 0423 { 0424 case TERMINAL: 0425 displayCommandResult(json, withColor, baseColor); 0426 break; 0427 case SVG: 0428 out << displaySVG(json, withColor) << "\n"; 0429 break; 0430 case BOT: 0431 case MARKDOWN: 0432 displayMarkdown(json); 0433 break; 0434 case TEXT: 0435 displayCommandResult(json, false, baseColor); 0436 break; 0437 case JSON: 0438 displayJSon(json); 0439 break; 0440 #ifdef PAINTER_OP 0441 case IMAGE: 0442 displayImage(json, withColor); 0443 break; 0444 #endif 0445 } 0446 if(!filePath.isEmpty()) 0447 { 0448 parser.writeDownDotTree(filePath); 0449 } 0450 } 0451 else 0452 rt= 1; 0453 } 0454 return rt; 0455 } 0456 0457 int main(int argc, char* argv[]) 0458 { 0459 #ifdef PAINTER_OP 0460 QGuiApplication a(argc, argv); 0461 #else 0462 QCoreApplication a(argc, argv); 0463 #endif 0464 0465 QStringList commands; 0466 QString cmd; 0467 QString dotFileStr; 0468 bool colorb= true; 0469 QSettings settings("rolisteam", "diceparser"); 0470 QString formatColor; 0471 EXPORTFORMAT format= TERMINAL; 0472 0473 QCommandLineParser optionParser; 0474 QCommandLineOption color(QStringList() << "c" 0475 << "color-off", 0476 "Disable color to highlight result"); 0477 QCommandLineOption version(QStringList() << "v" 0478 << "version", 0479 "Show the version and quit."); 0480 QCommandLineOption reset(QStringList() << "reset-settings", "Erase the settings and use the default parameters"); 0481 QCommandLineOption alias(QStringList() << "a" 0482 << "alias", 0483 "path to alias json files: <aliasfile>", "aliasfile"); 0484 0485 QCommandLineOption aliasData(QStringList() << "alias-data", "alias in json data <aliasdata>", "aliasdata"); 0486 0487 QCommandLineOption character(QStringList() << "s" 0488 << "charactersheet", 0489 "set Parameters to simulate character sheet: <sheetfile>", "sheetfile"); 0490 QCommandLineOption markdown(QStringList() << "m" 0491 << "markdown", 0492 "The output is formatted in markdown."); 0493 QCommandLineOption bot(QStringList() << "b" 0494 << "bot", 0495 "Discord bot."); 0496 QCommandLineOption svg(QStringList() << "g" 0497 << "svg", 0498 "The output is formatted in svg."); 0499 QCommandLineOption outColor(QStringList() << "C" 0500 << "color", 0501 "Use color for result: <color>", "color"); 0502 QCommandLineOption json(QStringList() << "j" 0503 << "json", 0504 "The output is formatted in json."); 0505 QCommandLineOption line(QStringList() << "l" 0506 << "line", 0507 "The output is in one line [default]."); 0508 QCommandLineOption dotFile(QStringList() << "d" 0509 << "dot-file", 0510 "Instead of rolling dice, generate the execution tree and write it in " 0511 "<dotfile>", 0512 "dotfile"); 0513 QCommandLineOption translation(QStringList() << "t" 0514 << "translation", 0515 "path to the translation file: <translationfile>", "translationfile"); 0516 QCommandLineOption help(QStringList() << "h" 0517 << "help", 0518 "Display this help"); 0519 0520 optionParser.addOption(color); 0521 optionParser.addOption(version); 0522 optionParser.addOption(reset); 0523 optionParser.addOption(dotFile); 0524 optionParser.addOption(alias); 0525 optionParser.addOption(aliasData); 0526 optionParser.addOption(character); 0527 optionParser.addOption(line); 0528 optionParser.addOption(markdown); 0529 optionParser.addOption(bot); 0530 optionParser.addOption(svg); 0531 optionParser.addOption(outColor); 0532 optionParser.addOption(json); 0533 optionParser.addOption(translation); 0534 optionParser.addOption(help); 0535 for(int i= 0; i < argc; ++i) 0536 { 0537 commands << QString::fromUtf8(argv[i]); 0538 } 0539 0540 optionParser.process(commands); 0541 0542 if(optionParser.isSet(color)) 0543 { 0544 commands.removeAt(0); 0545 colorb= false; 0546 } 0547 else if(optionParser.isSet(version)) 0548 { 0549 out << "Rolisteam DiceParser v1.0.0" 0550 << "\n"; 0551 out << "More Details: www.rolisteam.org" 0552 << "\n"; 0553 return 0; 0554 } 0555 else if(optionParser.isSet(reset)) 0556 { 0557 return 0; 0558 } 0559 else if(optionParser.isSet(dotFile)) 0560 { 0561 dotFileStr= optionParser.value(dotFile); 0562 } 0563 if(optionParser.isSet(markdown)) 0564 { 0565 format= MARKDOWN; 0566 } 0567 else if(optionParser.isSet(bot)) 0568 { 0569 format= BOT; 0570 } 0571 else if(optionParser.isSet(svg)) 0572 { 0573 format= SVG; 0574 } 0575 else if(optionParser.isSet(json)) 0576 { 0577 format= JSON; 0578 } 0579 else if(optionParser.isSet(line)) 0580 { 0581 format= TEXT; 0582 } 0583 if(optionParser.isSet(outColor)) 0584 { 0585 settings.setValue(colorkey, optionParser.value(outColor)); 0586 } 0587 0588 if(optionParser.isSet(help)) 0589 { 0590 cmd= "help"; 0591 } 0592 QStringList cmdList= optionParser.positionalArguments(); 0593 0594 QJsonArray aliases; 0595 if(optionParser.isSet(alias)) 0596 { 0597 auto aliasstr= optionParser.value(alias); 0598 0599 QFile file(aliasstr); 0600 0601 if(file.open(QIODevice::ReadOnly)) 0602 { 0603 QJsonDocument doc= QJsonDocument::fromJson(file.readAll()); 0604 aliases= doc.array(); 0605 } 0606 } 0607 else if(optionParser.isSet(aliasData)) 0608 { 0609 auto aliasstr= optionParser.value(aliasData); 0610 QJsonDocument doc= QJsonDocument::fromJson(aliasstr.toUtf8()); 0611 aliases= doc.array(); 0612 } 0613 0614 formatColor= settings.value(colorkey, "red").value<QString>(); 0615 returnValue= startDiceParsing(cmdList, colorb, formatColor, format, aliases, dotFileStr); 0616 if(optionParser.isSet(help)) 0617 { 0618 out << optionParser.helpText(); 0619 } 0620 return returnValue; 0621 }