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 }