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 }