File indexing completed on 2024-05-19 15:27:46

0001 /* This file is part of KGraphViewer.
0002    Copyright (C) 2006-2007 Gael de Chalendar <kleag@free.fr>
0003 
0004    KGraphViewer is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation, version 2.
0007 
0008    This program is distributed in the hope that it will be useful,
0009    but WITHOUT ANY WARRANTY; without even the implied warranty of
0010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011    General Public License for more details.
0012 
0013    You should have received a copy of the GNU General Public License
0014    along with this program; if not, write to the Free Software
0015    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0016    02110-1301, USA
0017 */
0018 
0019 #include "dotgrammar.h"
0020 #include "DotGraphParsingHelper.h"
0021 #include "dotdefaults.h"
0022 #include "dotgraph.h"
0023 #include "graphedge.h"
0024 #include "graphnode.h"
0025 #include "kgraphviewerlib_debug.h"
0026 
0027 #include <iostream>
0028 
0029 #include <QDebug>
0030 
0031 #include <QFile>
0032 
0033 #include <boost/spirit/include/classic_confix.hpp>
0034 #include <boost/throw_exception.hpp>
0035 namespace boost
0036 {
0037 void throw_exception(std::exception const &)
0038 {
0039 }
0040 }
0041 
0042 using namespace std;
0043 using namespace boost;
0044 using namespace boost::spirit::classic;
0045 
0046 using namespace KGraphViewer;
0047 
0048 #define KGV_MAX_ITEMS_TO_LOAD std::numeric_limits<size_t>::max()
0049 #define BOOST_SPIRIT_DEBUG 1
0050 
0051 DotGraphParsingHelper *phelper = nullptr;
0052 
0053 std::string therenderop;
0054 std::string thestr;
0055 
0056 void anychar(char const c);
0057 
0058 // keyword_p for C++
0059 // (for basic usage instead of std_p)
0060 const boost::spirit::classic::distinct_parser<> keyword_p("0-9a-zA-Z_");
0061 
0062 template<typename ScannerT> DotGrammar::definition<ScannerT>::definition(DotGrammar const & /*self*/)
0063 {
0064     graph = (!(keyword_p("strict")[&strict]) >> (keyword_p("graph")[&undigraph] | keyword_p("digraph")[&digraph]) >> !ID[&graphid] >> ch_p('{') >> !stmt_list >> ch_p('}'))[&finalactions];
0065     ID = ((((anychar_p - punct_p) | '_') >> *((anychar_p - punct_p) | '_')) | real_p | ('"' >> *((ch_p('\\') >> '"') | (anychar_p - '"')) >> '"') | (ch_p('<') >> *((anychar_p - '<' - '>') | tag) >> '>'));
0066     tag = ('<' >> *(anychar_p - '>') >> '>');
0067     stmt_list = stmt >> !(ch_p(';')) >> !(stmt_list);
0068     stmt = (attr_stmt | subgraph | edge_stmt | node_stmt | (ID >> '=' >> ID));
0069 
0070     attr_stmt = ((keyword_p("graph")[assign_a(phelper->attributed)] >> attr_list[&setattributedlist])[&setgraphattributes] | (keyword_p("node")[assign_a(phelper->attributed)] >> attr_list[&setattributedlist]) |
0071                  (keyword_p("edge")[assign_a(phelper->attributed)] >> attr_list[&setattributedlist]));
0072 
0073     attr_list = ch_p('[') >> !(a_list) >> ch_p(']');
0074     a_list = ((ID[&attrid] >> !('=' >> ID[&valid]))[&addattr] >> !(',' >> a_list));
0075     edge_stmt = ((node_id[&edgebound] | subgraph) >> edgeRHS >> !(attr_list[assign_a(phelper->attributed, "edge")]))[&pushAttrList][&setattributedlist][&createedges][&popAttrList];
0076     edgeRHS = edgeop[&checkedgeop] >> (node_id[&edgebound] | subgraph) >> !(edgeRHS);
0077     edgeop = str_p("->") | str_p("--");
0078     node_stmt = (node_id[&createnode] >> !(attr_list))[assign_a(phelper->attributed, "node")][&pushAttrList][&setattributedlist][&setnodeattributes][&popAttrList];
0079     node_id = (ID >> !(port));
0080     port = (ch_p(':') >> ID >> !(':' >> compass_pt)) | (':' >> compass_pt);
0081     subgraph = (!(keyword_p("subgraph") >> !(ID[&subgraphid])) >> ch_p('{')[&createsubgraph][&incrz][&pushAttrListC] >> stmt_list >> ch_p('}')[&decrz][&popAttrListC]) | (keyword_p("subgraph") >> ID[&subgraphid]);
0082     compass_pt = (keyword_p("n") | keyword_p("ne") | keyword_p("e") | keyword_p("se") | keyword_p("s") | keyword_p("sw") | keyword_p("w") | keyword_p("nw"));
0083 }
0084 
0085 void incrz(char const /*first*/)
0086 {
0087     if (phelper) {
0088         phelper->z++;
0089         if (phelper->z > phelper->maxZ) {
0090             phelper->maxZ = phelper->z;
0091         }
0092     }
0093 }
0094 
0095 void anychar(char const c)
0096 {
0097     Q_UNUSED(c);
0098     //   qCDebug(KGRAPHVIEWERLIB_LOG) << c;
0099 }
0100 
0101 void decrz(char const /*first*/)
0102 {
0103     if (phelper) {
0104         phelper->z--;
0105         phelper->gs = nullptr;
0106     }
0107 }
0108 
0109 void dump(char const *first, char const *last)
0110 {
0111     std::string str(first, last);
0112     qCWarning(KGRAPHVIEWERLIB_LOG) << ">>>> " << QString::fromStdString(str) << " <<<<";
0113 }
0114 
0115 void strict(char const * /*first*/, char const * /*last*/)
0116 {
0117     if (phelper)
0118         phelper->graph->strict(true);
0119 }
0120 
0121 void gotid(char const *first, char const *last)
0122 {
0123     std::string id(first, last);
0124     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Got ID  = '"<<QString::fromStdString(phelper->attrid)<<"'";
0125 }
0126 
0127 void undigraph(char const * /*first*/, char const * /*last*/)
0128 {
0129     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Setting graph as undirected";
0130     if (phelper)
0131         phelper->graph->directed(false);
0132 }
0133 
0134 void digraph(char const * /*first*/, char const * /*last*/)
0135 {
0136     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Setting graph as directed";
0137     if (phelper)
0138         phelper->graph->directed(true);
0139 }
0140 
0141 void graphid(char const *first, char const *last)
0142 {
0143     //   qCDebug(KGRAPHVIEWERLIB_LOG) << QString::fromStdString(std::string(first,last));
0144     if (phelper)
0145         phelper->graph->setId(QString::fromStdString(std::string(first, last)));
0146 }
0147 
0148 void attrid(char const *first, char const *last)
0149 {
0150     if (phelper) {
0151         std::string id(first, last);
0152         if (id.size() > 0 && id[0] == '"')
0153             id = id.substr(1);
0154         if (id.size() > 0 && id[id.size() - 1] == '"')
0155             id = id.substr(0, id.size() - 1);
0156         phelper->attrid = id;
0157         phelper->valid = "";
0158         //     qCDebug(KGRAPHVIEWERLIB_LOG) << "Got attr ID  = '"<<QString::fromStdString(phelper->attrid)<<"'";
0159     }
0160 }
0161 
0162 void subgraphid(char const *first, char const *last)
0163 {
0164     std::string id(first, last);
0165     //   qCDebug(KGRAPHVIEWERLIB_LOG) << QString::fromStdString(id);
0166     if (phelper) {
0167         if (id.size() > 0 && id[0] == '"')
0168             id = id.substr(1);
0169         if (id.size() > 0 && id[id.size() - 1] == '"')
0170             id = id.substr(0, id.size() - 1);
0171         phelper->subgraphid = id;
0172         //     qCDebug(KGRAPHVIEWERLIB_LOG) << "Got subgraph id = '"<<QString::fromStdString(phelper->subgraphid)<<"'";
0173     }
0174 }
0175 
0176 void valid(char const *first, char const *last)
0177 {
0178     std::string id(first, last);
0179     if (phelper) {
0180         if (id.size() > 0 && id[0] == '"')
0181             id = id.substr(1);
0182         if (id.size() > 0 && id[id.size() - 1] == '"')
0183             id = id.substr(0, id.size() - 1);
0184         phelper->valid = id;
0185         //     qCDebug(KGRAPHVIEWERLIB_LOG) << "Got attr val = '"<<QString::fromStdString(id)<<"'";
0186     }
0187 }
0188 
0189 void addattr(char const * /*first*/, char const * /*last*/)
0190 {
0191     if (phelper) {
0192         phelper->attributes.insert(std::make_pair(phelper->attrid, phelper->valid));
0193     }
0194 }
0195 
0196 void pushAttrListC(char const /*c*/)
0197 {
0198     pushAttrList(nullptr, nullptr);
0199 }
0200 
0201 void pushAttrList(char const * /*first*/, char const * /*last*/)
0202 {
0203     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Pushing attributes";
0204     if (phelper) {
0205         phelper->graphAttributesStack.push_back(phelper->graphAttributes);
0206         phelper->nodesAttributesStack.push_back(phelper->nodesAttributes);
0207         phelper->edgesAttributesStack.push_back(phelper->edgesAttributes);
0208     }
0209 }
0210 
0211 void popAttrListC(char const /*c*/)
0212 {
0213     popAttrList(nullptr, nullptr);
0214 }
0215 
0216 void popAttrList(char const * /*first*/, char const * /*last*/)
0217 {
0218     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Poping attributes";
0219     if (phelper) {
0220         phelper->graphAttributes = phelper->graphAttributesStack.back();
0221         phelper->graphAttributesStack.pop_back();
0222         phelper->nodesAttributes = phelper->nodesAttributesStack.back();
0223         phelper->nodesAttributesStack.pop_back();
0224         phelper->edgesAttributes = phelper->edgesAttributesStack.back();
0225         phelper->edgesAttributesStack.pop_back();
0226     }
0227     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Poped";
0228 }
0229 
0230 void createnode(char const *first, char const *last)
0231 {
0232     //   qCDebug(KGRAPHVIEWERLIB_LOG) << (void*)first << (void*)last << QString::fromStdString(std::string(first,last));
0233     if (phelper && first && last) {
0234         std::string id(first, last);
0235         if (id.size() > 0 && id[0] == '"')
0236             id = id.substr(1);
0237         if (id.size() > 0 && id[id.size() - 1] == '"')
0238             id = id.substr(0, id.size() - 1);
0239         phelper->createnode(id);
0240     }
0241 }
0242 
0243 void createsubgraph(char const /*c*/)
0244 {
0245     if (phelper) {
0246         phelper->createsubgraph();
0247     }
0248 }
0249 
0250 void setgraphattributes(char const * /*first*/, char const * /*last*/)
0251 {
0252     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "setgraphattributes with z = " << phelper->z;
0253     if (phelper) {
0254         if (phelper->z == 1) // main graph
0255         {
0256             phelper->setgraphattributes();
0257         } else {
0258             phelper->setsubgraphattributes();
0259         }
0260     }
0261 }
0262 
0263 void setnodeattributes(char const * /*first*/, char const * /*last*/)
0264 {
0265     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "setnodeattributes with z = " << phelper->z;
0266     if (phelper) {
0267         phelper->setnodeattributes();
0268     }
0269 }
0270 
0271 void setattributedlist(char const * /*first*/, char const * /*last*/)
0272 {
0273     if (phelper) {
0274         phelper->setattributedlist();
0275     }
0276 }
0277 
0278 void checkedgeop(char const *first, char const *last)
0279 {
0280     std::string str(first, last);
0281     if (phelper) {
0282         if (((phelper->graph->directed()) && (str == "->")) || ((!phelper->graph->directed()) && (str == "--")))
0283             return;
0284 
0285         qCWarning(KGRAPHVIEWERLIB_LOG) << "Error !! uncoherent relation : directed = '" << phelper->graph->directed() << "' and op = '" << QString::fromStdString(str) << "'";
0286     }
0287 }
0288 
0289 void edgebound(char const *first, char const *last)
0290 {
0291     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "edgebound: " << QString::fromStdString(std::string(first,last));
0292     if (phelper) {
0293         std::string id(first, last);
0294         if (id.size() > 0 && id[0] == '"')
0295             id = id.substr(1);
0296         if (id.size() > 0 && id[id.size() - 1] == '"')
0297             id = id.substr(0, id.size() - 1);
0298         phelper->edgebound(id);
0299     }
0300 }
0301 
0302 void createedges(char const * /*first*/, char const * /*last*/)
0303 {
0304     if (phelper) {
0305         phelper->createedges();
0306     }
0307 }
0308 
0309 void finalactions(char const * /*first*/, char const * /*last*/)
0310 {
0311     if (phelper) {
0312         phelper->finalactions();
0313     }
0314 }
0315 
0316 bool parse_point(char const *str, QPoint &p)
0317 {
0318     int x, y;
0319     bool res;
0320     res = parse(str, (int_p[assign_a(x)] >> ',' >> int_p[assign_a(y)]), +space_p).full;
0321     if (!res)
0322         return false;
0323     p = QPoint(x, y);
0324     return true;
0325 }
0326 
0327 bool parse_numeric_color(char const *str, QColor &c)
0328 {
0329     if (str == nullptr)
0330         return false;
0331     int r, g, b, a;
0332     bool res;
0333     uint_parser<unsigned, 16, 2, 2> hex2digits_p;
0334     res = parse(str, (ch_p('#') >> hex2digits_p[assign_a(r)] >> hex2digits_p[assign_a(g)] >> hex2digits_p[assign_a(b)] >> !hex2digits_p[assign_a(a)]), +space_p).full;
0335     if (res) {
0336         c.setRgb(r, g, b);
0337         return true;
0338     }
0339 
0340     double h, s, v;
0341     res = parse(str, (real_p[assign_a(h)] >> !ch_p(',') >> real_p[assign_a(s)] >> !ch_p(',') >> real_p[assign_a(v)]), +space_p).full;
0342     if (res) {
0343         c.setHsv(int(255 * h), int(255 * s), int(255 * v));
0344         return true;
0345     }
0346     return false;
0347 }
0348 
0349 bool parse_real(char const *str, double &d)
0350 {
0351     return parse(str, (real_p[assign_a(d)]), +space_p).full;
0352 }
0353 
0354 bool parse_integers(char const *str, std::vector<int> &v)
0355 {
0356     return parse(str, (int_p[push_back_a(v)] >> *(',' >> int_p[push_back_a(v)])), +space_p).full;
0357 }
0358 
0359 bool parse_spline(char const *str, QVector<QPair<float, float>> &points)
0360 {
0361     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Parsing spline..." << QString::fromStdString(str);
0362     char e = 'n', s = 'n';
0363     int sx, sy, ex, ey;
0364     QPair<float, float> p;
0365     bool res;
0366     res = parse(str,
0367                 (!(ch_p('e')[assign_a(e)] >> ',' >> int_p[assign_a(ex)] >> ',' >> int_p[assign_a(ey)]) >> !(ch_p('s')[assign_a(s)] >> ',' >> int_p[assign_a(sx)] >> ',' >> int_p[assign_a(sy)]) >>
0368                  ((int_p[assign_a(p.first)] >> ',' >> int_p[assign_a(p.second)]))[push_back_a(points, p)] >>
0369                  +((int_p[assign_a(p.first)] >> ',' >> int_p[assign_a(p.second)])[push_back_a(points, p)] >> (int_p[assign_a(p.first)] >> ',' >> int_p[assign_a(p.second)])[push_back_a(points, p)] >>
0370                    (int_p[assign_a(p.first)] >> ',' >> int_p[assign_a(p.second)])[push_back_a(points, p)])),
0371                 +space_p)
0372               .full;
0373     if (!res)
0374         return false;
0375     if (s == 's') {
0376         //     qCDebug(KGRAPHVIEWERLIB_LOG) << "inserting the s point";
0377         points.insert(points.begin(), qMakePair(float(sx), float(sy)));
0378     }
0379     if (e == 'e') {
0380         //     points.insert(points.begin(), qMakePair(float(ex),float(ey)));
0381     }
0382     return true;
0383 }
0384 
0385 DotRenderOp renderop;
0386 
0387 DotRenderOpVec *renderopvec = nullptr;
0388 
0389 void init_op()
0390 {
0391     renderop = DotRenderOp();
0392 }
0393 
0394 void valid_op(char const *first, char const *last)
0395 {
0396     std::string s(first, last);
0397     renderop.renderop = QString::fromUtf8(therenderop.c_str());
0398     renderop.str = QString::fromUtf8(thestr.c_str());
0399 
0400     //   qCDebug(KGRAPHVIEWERLIB_LOG) << "Validating render operation '"<<QString::fromStdString(s)<<"': '"<<renderop.renderop<<"/"<<renderop.str<<"'";
0401     renderopvec->push_back(renderop);
0402     renderop.renderop = "";
0403     renderop.integers = QList<int>();
0404     renderop.str = "";
0405 }
0406 
0407 bool parse_renderop(const std::string &str, DotRenderOpVec &arenderopvec)
0408 {
0409     //   qCDebug(KGRAPHVIEWERLIB_LOG) << QString::fromUtf8(str.c_str()) << str.size();
0410     if (str.empty()) {
0411         return false;
0412     }
0413     init_op();
0414     renderopvec = &arenderopvec;
0415     bool res;
0416     int c;
0417     res = parse(str.c_str(),
0418                 (+(((ch_p('E') | ch_p('e'))[assign_a(therenderop)] >> +space_p >> repeat_p(4)[int_p[push_back_a(renderop.integers)] >> !((ch_p(',') | ch_p('.')) >> int_p) >> +space_p])[&valid_op] |
0419                    ((ch_p('P') | ch_p('p') | ch_p('L') | ch_p('B') | ch_p('b'))[assign_a(therenderop)] >> +space_p >> int_p[assign_a(c)][push_back_a(renderop.integers)] >> +space_p >>
0420                     repeat_p(boost::ref(c))[int_p[push_back_a(renderop.integers)] >> !((ch_p(',') | ch_p('.')) >> int_p) >> +space_p >> int_p[push_back_a(renderop.integers)] >> !((ch_p(',') | ch_p('.')) >> int_p) >> +space_p])[&valid_op]
0421                    // "T 1537 228 0 40 9 -#1 (== 0) T 1537 217 0 90 19 -MAIN:./main/main.pl "
0422                    // T x y j w n -b1b2...bn    Text drawn using the baseline point (x,y). The text consists of the n bytes following '-'. The text should be left-aligned (centered, right-aligned) on the point if j is -1 (0, 1),
0423                    // respectively. The value w gives the width of the text as computed by the library. I x y w h n -b1b2...bn  Externally-specified image drawn in the box with lower left corner (x,y) and upper right corner (x+w,y+h). The
0424                    // name of the image consists of the n bytes following '-'. This is usually a bitmap image. Note that the image size, even when converted from pixels to points, might be different from the required size (w,h). It is
0425                    // assumed the renderer will perform the necessary scaling. (1.2)
0426                    | ((ch_p('T') | ch_p('I'))[assign_a(therenderop)] >> +space_p >> repeat_p(4)[int_p[push_back_a(renderop.integers)] >> !((ch_p(',') | ch_p('.')) >> int_p) >> +space_p] >> int_p[assign_a(c)] >> +space_p >> '-' >>
0427                       (repeat_p(boost::ref(c))[anychar_p])[assign_a(thestr)] >> +space_p)[&valid_op]
0428                    // c 9 -#000000ff
0429                    | ((ch_p('C') | ch_p('c') | ch_p('S'))[assign_a(therenderop)] >> +space_p >> int_p[assign_a(c)] >> +space_p >> '-' >> (repeat_p(boost::ref(c))[anychar_p])[assign_a(thestr)] >> +space_p)[&valid_op]
0430                    // t 0
0431                    // t f   Set font characteristics. The integer f is the OR of BOLD=1, ITALIC=2, UNDERLINE=4, SUPERSCRIPT=8, SUBSCRIPT=16, (1.5) STRIKE-THROUGH=32 (1.6), and OVERLINE=64 (1.7).
0432                    | ((ch_p('t'))[assign_a(therenderop)] >> +space_p >> int_p[assign_a(c)] >> +space_p)[&valid_op]
0433                    // F 14,000000 11 -Times-Roman
0434                    | (ch_p('F')[assign_a(therenderop)] >> +space_p >> int_p[push_back_a(renderop.integers)] >> !((ch_p(',') | ch_p('.')) >> int_p) >> +space_p >> int_p[assign_a(c)] >> +space_p >> '-' >>
0435                       (repeat_p(boost::ref(c))[anychar_p])[assign_a(thestr)] >> +space_p)[&valid_op])) >>
0436                     !end_p)
0437               .full;
0438     if (res == false) {
0439         qCWarning(KGRAPHVIEWERLIB_LOG) << "ERROR: parse_renderop failed on " << QString::fromStdString(str);
0440         qCWarning(KGRAPHVIEWERLIB_LOG) << "       Last renderop string is " << QString::fromStdString(str.c_str());
0441     }
0442     //   delete renderop; renderop = 0;
0443     return res;
0444 }
0445 
0446 bool parse(const std::string &str)
0447 {
0448     DotGrammar g;
0449     return boost::spirit::classic::parse(str.c_str(), g >> end_p, (+boost::spirit::classic::space_p | boost::spirit::classic::comment_p("/*", "*/"))).full;
0450 }