The models, scripts, and results of the benchmarks performed for a Half Reification Journal paper
at develop 1423 lines 46 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Guido Tack <guido.tack@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#include <minizinc/config.hh> 13#include <minizinc/copy.hh> 14#include <minizinc/eval_par.hh> 15#include <minizinc/htmlprinter.hh> 16#include <minizinc/model.hh> 17#include <minizinc/prettyprinter.hh> 18 19#include <cctype> 20#include <sstream> 21#include <utility> 22 23namespace MiniZinc { 24 25namespace HtmlDocOutput { 26 27// Trim leading space: 28// - always trim first line completely 29// - second line defines the base indentation 30std::string trim(const std::string& s0) { 31 std::string s = s0; 32 // remove carriage returns 33 size_t j = 0; 34 for (size_t i = 0; i < s.size(); i++) { 35 if (s[i] != '\r') { 36 s[j++] = s[i]; 37 } 38 } 39 s.resize(j); 40 size_t first_line_indent = s.find_first_not_of(" \t"); 41 if (first_line_indent == std::string::npos) { 42 return ""; 43 } 44 size_t first_nl = s.find('\n'); 45 std::ostringstream oss; 46 if (first_line_indent == first_nl) { 47 // first line is empty 48 oss << "\n"; 49 } else { 50 // strip first line 51 size_t end_of_first_line = 52 first_nl == std::string::npos ? std::string::npos : first_nl - first_line_indent + 1; 53 oss << s.substr(first_line_indent, end_of_first_line); 54 } 55 if (first_nl == std::string::npos) { 56 return oss.str(); 57 } 58 size_t unindent = s.find_first_not_of(" \t", first_nl + 1); 59 if (unindent == std::string::npos) { 60 return oss.str(); 61 } 62 size_t pos = s.find('\n', first_nl + 1); 63 if (unindent == 0 || unindent > pos) { 64 oss << s.substr(first_nl + 1, std::string::npos); 65 return oss.str(); 66 } 67 size_t lastpos = unindent; 68 while (pos != std::string::npos) { 69 oss << s.substr(lastpos, pos - lastpos) << "\n"; 70 size_t next_indent = s.find_first_not_of(" \t", pos + 1); 71 if (next_indent == std::string::npos || next_indent - (pos + 1) < unindent) { 72 lastpos = next_indent; 73 } else { 74 lastpos = pos + 1 + unindent; 75 } 76 pos = (lastpos == std::string::npos ? lastpos : s.find('\n', lastpos)); 77 } 78 if (lastpos != std::string::npos) { 79 oss << s.substr(lastpos, std::string::npos); 80 } 81 return oss.str(); 82} 83 84class DocItem { 85public: 86 enum DocType { T_PAR = 0, T_VAR = 1, T_FUN = 2 }; 87 DocItem(const DocType& t0, std::string id0, std::string sig0, std::string doc0) 88 : t(t0), id(std::move(id0)), sig(std::move(sig0)), doc(std::move(doc0)) {} 89 DocType t; 90 std::string id; 91 std::string sig; 92 std::string doc; 93}; 94 95typedef std::unordered_map<FunctionI*, std::string> FunMap; 96 97class Group; 98 99class GroupMap { 100public: 101 typedef std::vector<Group*> Map; 102 Map m; 103 ~GroupMap(); 104 Map::iterator find(const std::string& n); 105}; 106 107class Group { 108public: 109 Group(std::string name0, std::string fullPath0) 110 : name(std::move(name0)), fullPath(std::move(fullPath0)) {} 111 std::string name; 112 std::string fullPath; 113 std::string desc; 114 std::string htmlName; 115 GroupMap subgroups; 116 std::vector<DocItem> items; 117 118 std::string getAnchor(int level, int indivFileLevel) const { 119 if (level < indivFileLevel) { 120 return fullPath + ".html"; 121 } 122 return "#" + fullPath; 123 } 124 125 std::string toHTML(int level, int indivFileLevel, Group* parent, unsigned int idx, 126 const std::string& basename, bool generateIndex) { 127 std::ostringstream oss; 128 129 int realLevel = (level < indivFileLevel) ? 0 : level - indivFileLevel; 130 oss << "<div class='mzn-group-level-" << realLevel << "'>\n"; 131 if (parent != nullptr) { 132 oss << "<div class='mzn-group-nav'>"; 133 if (idx > 0) { 134 oss << "<a class='mzn-nav-prev' href='" 135 << parent->subgroups.m[idx - 1]->getAnchor(level - 1, indivFileLevel) << "' title='" 136 << parent->subgroups.m[idx - 1]->htmlName << "'>&#8656;</a> "; 137 } 138 oss << "<a class='mzn-nav-up' href='" << parent->getAnchor(level - 1, indivFileLevel) 139 << "' title='" << parent->htmlName << "'>&#8679;</a> "; 140 if (idx < parent->subgroups.m.size() - 1) { 141 oss << "<a class='mzn-nav-next' href='" 142 << parent->subgroups.m[idx + 1]->getAnchor(level - 1, indivFileLevel) << "' title='" 143 << parent->subgroups.m[idx + 1]->htmlName << "'>&#8658;</a> "; 144 } 145 if (generateIndex) { 146 oss << "<a href='doc-index.html'>Index</a>\n"; 147 } 148 if (!items.empty()) { 149 oss << "<a href='javascript:void(0)' onclick='revealAll()' class='mzn-nav-text'>reveal " 150 "all</a>\n"; 151 oss << "<a href='javascript:void(0)' onclick='hideAll()' class='mzn-nav-text'>hide " 152 "all</a>\n"; 153 } 154 oss << "</div>"; 155 } 156 if (!htmlName.empty()) { 157 oss << "<div class='mzn-group-name'><a name='" << fullPath << "'>" << htmlName 158 << "</a></div>\n"; 159 oss << "<div class='mzn-group-desc'>\n" << desc << "</div>\n"; 160 } 161 162 if (!subgroups.m.empty()) { 163 oss << "<p>Sections:</p>\n"; 164 oss << "<ul>\n"; 165 for (auto& it : subgroups.m) { 166 oss << "<li><a href='" << it->getAnchor(level, indivFileLevel) << "'>" << it->htmlName 167 << "</a>\n"; 168 169 if (it->htmlName.empty()) { 170 std::cerr << "Warning: undocumented group " << it->fullPath << "\n"; 171 } 172 } 173 oss << "</ul>\n"; 174 if (parent == nullptr && generateIndex) { 175 oss << "<p><a href='doc-index.html'>Index</a></p>\n"; 176 } 177 if (!items.empty()) { 178 oss << "<p>Declarations in this section:</p>\n"; 179 } 180 } 181 182 struct SortById { 183 bool operator()(const DocItem& i0, const DocItem& i1) { 184 return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); 185 } 186 } _cmp; 187 std::stable_sort(items.begin(), items.end(), _cmp); 188 189 int cur_t = -1; 190 const char* dt[] = {"par", "var", "fun"}; 191 const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; 192 for (const auto& item : items) { 193 if (item.t != cur_t) { 194 if (cur_t != -1) { 195 oss << "</div>\n"; 196 } 197 cur_t = item.t; 198 oss << "<div class='mzn-decl-type-" << dt[cur_t] << "'>\n"; 199 oss << "<div class='mzn-decl-type-heading'>" << dt_desc[cur_t] << "</div>\n"; 200 } 201 oss << item.doc; 202 } 203 if (cur_t != -1) { 204 oss << "</div>\n"; 205 } 206 207 if (level >= indivFileLevel) { 208 for (unsigned int i = 0; i < subgroups.m.size(); i++) { 209 oss << subgroups.m[i]->toHTML(level + 1, indivFileLevel, this, i, basename, generateIndex); 210 } 211 } 212 213 oss << "</div>"; 214 return oss.str(); 215 } 216 217 static std::string rstHeading(const std::string& s, int level) { 218 std::vector<char> levelChar({'#', '=', '-', '^', '+', '"'}); 219 std::ostringstream oss; 220 oss << s << "\n"; 221 for (int i = 0; i < s.size(); i++) { 222 oss << levelChar[level]; 223 } 224 oss << "\n\n"; 225 return oss.str(); 226 } 227 228 std::string toRST(int level) { 229 std::ostringstream oss; 230 if (!htmlName.empty()) { 231 if (level == 0) { 232 oss << ".. _ch-lib-" << name << ":\n\n"; 233 } 234 oss << rstHeading(htmlName, level); 235 oss << HtmlDocOutput::trim(desc) << "\n\n"; 236 } 237 for (auto& i : subgroups.m) { 238 oss << i->toRST(level + 1); 239 } 240 if (!items.empty()) { 241 if (!subgroups.m.empty()) { 242 oss << rstHeading("Other declarations", level + 1); 243 } 244 struct SortById { 245 bool operator()(const DocItem& i0, const DocItem& i1) { 246 return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); 247 } 248 } _cmp; 249 std::stable_sort(items.begin(), items.end(), _cmp); 250 251 int cur_t = -1; 252 int nHeadings = 0; 253 for (const auto& item : items) { 254 if (item.t != cur_t) { 255 cur_t = item.t; 256 nHeadings++; 257 } 258 } 259 cur_t = -1; 260 const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; 261 for (const auto& item : items) { 262 if (item.t != cur_t) { 263 cur_t = item.t; 264 if (nHeadings > 1) { 265 oss << rstHeading(dt_desc[cur_t], subgroups.m.empty() ? level + 1 : level + 2); 266 } 267 } 268 oss << item.doc; 269 } 270 } 271 return oss.str(); 272 } 273}; 274 275GroupMap::~GroupMap() { 276 for (auto& it : m) { 277 delete it; 278 } 279} 280GroupMap::Map::iterator GroupMap::find(const std::string& n) { 281 for (auto it = m.begin(); it != m.end(); ++it) { 282 if ((*it)->name == n) { 283 return it; 284 } 285 } 286 return m.end(); 287} 288 289void add_to_group(Group& gm, const std::string& group, DocItem& di) { 290 std::vector<std::string> subgroups; 291 size_t lastpos = 0; 292 size_t pos = group.find('.'); 293 while (pos != std::string::npos) { 294 subgroups.push_back(group.substr(lastpos, pos - lastpos)); 295 lastpos = pos + 1; 296 pos = group.find('.', lastpos); 297 } 298 subgroups.push_back(group.substr(lastpos, std::string::npos)); 299 300 GroupMap* cgm = &gm.subgroups; 301 std::string gpath(gm.fullPath); 302 for (unsigned int i = 0; i < subgroups.size(); i++) { 303 gpath += "-"; 304 gpath += subgroups[i]; 305 if (cgm->find(subgroups[i]) == cgm->m.end()) { 306 cgm->m.push_back(new Group(subgroups[i], gpath)); 307 } 308 Group& g = **cgm->find(subgroups[i]); 309 if (i == subgroups.size() - 1) { 310 g.items.push_back(di); 311 } else { 312 cgm = &g.subgroups; 313 } 314 } 315} 316 317void set_group_desc(Group& maingroup, const std::string& group, const std::string& htmlName, 318 const std::string& s) { 319 if (group == "MAIN") { 320 if (!maingroup.htmlName.empty()) { 321 std::cerr << "Warning: two descriptions for group `" << group << "'\n"; 322 } 323 maingroup.htmlName = htmlName; 324 maingroup.desc = s; 325 return; 326 } 327 328 std::vector<std::string> subgroups; 329 size_t lastpos = 0; 330 size_t pos = group.find('.'); 331 while (pos != std::string::npos) { 332 subgroups.push_back(group.substr(lastpos, pos - lastpos)); 333 lastpos = pos + 1; 334 pos = group.find('.', lastpos); 335 } 336 subgroups.push_back(group.substr(lastpos, std::string::npos)); 337 338 GroupMap* cgm = &maingroup.subgroups; 339 std::string gpath(maingroup.fullPath); 340 for (unsigned int i = 0; i < subgroups.size(); i++) { 341 gpath += "-"; 342 gpath += subgroups[i]; 343 if (cgm->find(subgroups[i]) == cgm->m.end()) { 344 cgm->m.push_back(new Group(subgroups[i], gpath)); 345 } 346 Group& g = **cgm->find(subgroups[i]); 347 if (i == subgroups.size() - 1) { 348 if (!g.htmlName.empty()) { 349 std::cerr << "Warning: two descriptions for group `" << group << "'\n"; 350 } 351 g.htmlName = htmlName; 352 g.desc = s; 353 } else { 354 cgm = &g.subgroups; 355 } 356 } 357} 358 359std::string extract_arg_word(std::string& s, size_t n) { 360 size_t start = n; 361 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 362 start++; 363 } 364 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 365 start++; 366 } 367 size_t end = start + 1; 368 while (end < s.size() && ((isalnum(s[end]) != 0) || s[end] == '_' || s[end] == '.')) { 369 end++; 370 } 371 std::string ret = s.substr(start, end - start); 372 s = s.substr(end, std::string::npos); 373 return ret; 374} 375 376std::string make_html_id(const std::string& ident) { 377 std::ostringstream oss; 378 oss << "I"; 379 bool prevWasSym = false; 380 for (char i : ident) { 381 bool isSym = true; 382 switch (i) { 383 case '!': 384 oss << "-ex"; 385 break; 386 case '=': 387 oss << "-eq"; 388 break; 389 case '*': 390 oss << "-as"; 391 break; 392 case '+': 393 oss << "-pl"; 394 break; 395 case '-': 396 oss << "-mi"; 397 break; 398 case '>': 399 oss << "-gr"; 400 break; 401 case '<': 402 oss << "-lt"; 403 break; 404 case '/': 405 oss << "-dv"; 406 break; 407 case '\\': 408 oss << "-bs"; 409 break; 410 case '~': 411 oss << "-tl"; 412 break; 413 case '\'': 414 oss << "-tk"; 415 break; 416 case ' ': 417 case '\t': 418 case '\n': 419 break; 420 case ':': 421 oss << "-cl"; 422 break; 423 case '[': 424 oss << "-bo"; 425 break; 426 case ']': 427 oss << "-bc"; 428 break; 429 case '$': 430 oss << "-dd"; 431 break; 432 case '(': 433 oss << "-po"; 434 break; 435 case ')': 436 oss << "-pc"; 437 break; 438 case ',': 439 oss << "-cm"; 440 break; 441 default: 442 oss << (prevWasSym ? "-" : "") << i; 443 isSym = false; 444 break; 445 } 446 prevWasSym = isSym; 447 } 448 return oss.str(); 449} 450 451} // namespace HtmlDocOutput 452 453class CollectFunctionsVisitor : public ItemVisitor { 454protected: 455 EnvI& _env; 456 HtmlDocOutput::FunMap& _funmap; 457 bool _includeStdLib; 458 459public: 460 CollectFunctionsVisitor(EnvI& env, HtmlDocOutput::FunMap& funmap, bool includeStdLib) 461 : _env(env), _funmap(funmap), _includeStdLib(includeStdLib) {} 462 bool enterModel(Model* m) const { return _includeStdLib || m->filename() != "stdlib.mzn"; } 463 void vFunctionI(FunctionI* fi) { 464 if (Call* docstring = 465 Expression::dynamicCast<Call>(get_annotation(fi->ann(), constants().ann.doc_comment))) { 466 std::string ds = eval_string(_env, docstring->arg(0)); 467 std::string group("main"); 468 size_t group_idx = ds.find("@group"); 469 if (group_idx != std::string::npos) { 470 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 471 } 472 _funmap.insert(std::make_pair(fi, group)); 473 } 474 } 475}; 476 477class PrintHtmlVisitor : public ItemVisitor { 478protected: 479 EnvI& _env; 480 HtmlDocOutput::Group& _maingroup; 481 HtmlDocOutput::FunMap& _funmap; 482 bool _includeStdLib; 483 484 static std::vector<std::string> replaceArgs(std::string& s) { 485 std::vector<std::string> replacements; 486 std::ostringstream oss; 487 size_t lastpos = 0; 488 size_t pos = std::min(s.find("\\a"), s.find("\\p")); 489 size_t mathjax_open = s.find("\\("); 490 size_t mathjax_close = s.rfind("\\)"); 491 if (pos == std::string::npos) { 492 return replacements; 493 } 494 while (pos != std::string::npos) { 495 oss << s.substr(lastpos, pos - lastpos); 496 size_t start = pos; 497 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 498 start++; 499 } 500 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 501 start++; 502 } 503 size_t end = start + 1; 504 while (end < s.size() && ((isalnum(s[end]) != 0) || s[end] == '_')) { 505 end++; 506 } 507 if (s[pos + 1] == 'a') { 508 replacements.push_back(s.substr(start, end - start)); 509 if (pos >= mathjax_open && pos <= mathjax_close) { 510 oss << "{\\bf " << replacements.back() << "}"; 511 } else { 512 oss << "<span class='mzn-arg'>" << replacements.back() << "</span>"; 513 } 514 } else { 515 if (pos >= mathjax_open && pos <= mathjax_close) { 516 oss << "{\\bf " << s.substr(start, end - start) << "}"; 517 } else { 518 oss << "<span class='mzn-parm'>" << s.substr(start, end - start) << "</span>"; 519 } 520 } 521 lastpos = end; 522 pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); 523 } 524 oss << s.substr(lastpos, std::string::npos); 525 s = oss.str(); 526 return replacements; 527 } 528 529 static std::pair<std::string, std::string> extractArgLine(std::string& s, size_t n) { 530 size_t start = n; 531 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 532 start++; 533 } 534 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 535 start++; 536 } 537 size_t end = start + 1; 538 while (end < s.size() && s[end] != ':') { 539 end++; 540 } 541 std::string arg = s.substr(start, end - start); 542 size_t doc_start = end + 1; 543 while (end < s.size() && s[end] != '\n') { 544 end++; 545 } 546 std::string ret = s.substr(doc_start, end - doc_start); 547 replaceArgs(ret); 548 s = s.substr(0, n) + s.substr(end, std::string::npos); 549 return make_pair(arg, ret); 550 } 551 552 static std::string addHTML(const std::string& s) { 553 std::ostringstream oss; 554 size_t lastpos = 0; 555 size_t pos = s.find('\n'); 556 bool inUl = false; 557 oss << "<p>\n"; 558 while (pos != std::string::npos) { 559 oss << s.substr(lastpos, pos - lastpos); 560 size_t next = std::min(s.find('\n', pos + 1), s.find('-', pos + 1)); 561 if (next == std::string::npos) { 562 lastpos = pos + 1; 563 break; 564 } 565 bool allwhite = true; 566 for (size_t cur = pos + 1; cur < next; cur++) { 567 if (s[cur] != ' ' && s[cur] != '\t') { 568 allwhite = false; 569 break; 570 } 571 } 572 if (allwhite) { 573 if (s[next] == '-') { 574 if (!inUl) { 575 oss << "<ul>\n"; 576 inUl = true; 577 } 578 oss << "<li>"; 579 } else { 580 if (inUl) { 581 oss << "</ul>\n"; 582 inUl = false; 583 } else { 584 oss << "</p><p>\n"; 585 } 586 } 587 lastpos = next + 1; 588 pos = s.find('\n', lastpos); 589 } else { 590 lastpos = pos + 1; 591 if (s[pos] == '\n') { 592 oss << " "; 593 } 594 if (s[next] == '-') { 595 pos = s.find('\n', next + 1); 596 } else { 597 pos = next; 598 } 599 } 600 } 601 oss << s.substr(lastpos, std::string::npos); 602 if (inUl) { 603 oss << "</ul>\n"; 604 } 605 oss << "</p>\n"; 606 return oss.str(); 607 } 608 609public: 610 PrintHtmlVisitor(EnvI& env, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, 611 bool includeStdLib) 612 : _env(env), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} 613 bool enterModel(Model* m) { 614 if (!_includeStdLib && m->filename() == "stdlib.mzn") { 615 return false; 616 } 617 const std::string& dc = m->docComment(); 618 if (!dc.empty()) { 619 size_t gpos = dc.find("@groupdef"); 620 while (gpos != std::string::npos) { 621 size_t start = gpos; 622 while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') { 623 start++; 624 } 625 while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) { 626 start++; 627 } 628 size_t end = start + 1; 629 while (end < dc.size() && ((isalnum(dc[end]) != 0) || dc[end] == '_' || dc[end] == '.')) { 630 end++; 631 } 632 std::string groupName = dc.substr(start, end - start); 633 size_t doc_start = end + 1; 634 while (end < dc.size() && dc[end] != '\n') { 635 end++; 636 } 637 std::string groupHTMLName = dc.substr(doc_start, end - doc_start); 638 639 size_t next = dc.find("@groupdef", gpos + 1); 640 HtmlDocOutput::set_group_desc( 641 _maingroup, groupName, groupHTMLName, 642 addHTML(dc.substr(end, next == std::string::npos ? next : next - end))); 643 gpos = next; 644 } 645 } 646 return true; 647 } 648 /// Visit variable declaration 649 void vVarDeclI(VarDeclI* vdi) { 650 if (Call* docstring = Expression::dynamicCast<Call>( 651 get_annotation(vdi->e()->ann(), constants().ann.doc_comment))) { 652 std::string ds = eval_string(_env, docstring->arg(0)); 653 std::string group("main"); 654 size_t group_idx = ds.find("@group"); 655 if (group_idx != std::string::npos) { 656 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 657 } 658 659 std::ostringstream os; 660 std::string sig = 661 vdi->e()->type().toString(_env) + " " + std::string(vdi->e()->id()->str().c_str()); 662 os << "<div class='mzn-vardecl' id='" << HtmlDocOutput::make_html_id(sig) << "'>\n"; 663 os << "<div class='mzn-vardecl-code'>\n"; 664 if (vdi->e()->ti()->type() == Type::ann()) { 665 os << "<span class='mzn-kw'>annotation</span> "; 666 os << "<span class='mzn-fn-id'>" << *vdi->e()->id() << "</span>"; 667 } else { 668 os << *vdi->e()->ti() << ": " << *vdi->e()->id(); 669 } 670 os << "</div><div class='mzn-vardecl-doc'>\n"; 671 os << addHTML(ds); 672 os << "</div></div>"; 673 GCLock lock; 674 HtmlDocOutput::DocItem di( 675 vdi->e()->type().isPar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, 676 sig, sig, os.str()); 677 HtmlDocOutput::add_to_group(_maingroup, group, di); 678 } 679 } 680 /// Visit function item 681 void vFunctionI(FunctionI* fi) { 682 if (Call* docstring = 683 Expression::dynamicCast<Call>(get_annotation(fi->ann(), constants().ann.doc_comment))) { 684 std::string ds = eval_string(_env, docstring->arg(0)); 685 std::string group("main"); 686 size_t group_idx = ds.find("@group"); 687 if (group_idx != std::string::npos) { 688 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 689 } 690 691 size_t param_idx = ds.find("@param"); 692 std::vector<std::pair<std::string, std::string> > params; 693 while (param_idx != std::string::npos) { 694 params.push_back(extractArgLine(ds, param_idx)); 695 param_idx = ds.find("@param"); 696 } 697 698 std::vector<std::string> args = replaceArgs(ds); 699 700 std::unordered_set<std::string> allArgs; 701 for (auto& arg : args) { 702 allArgs.insert(arg); 703 } 704 for (auto& param : params) { 705 allArgs.insert(param.first); 706 } 707 708 GCLock lock; 709 for (unsigned int i = 0; i < fi->paramCount(); i++) { 710 std::string param(fi->param(i)->id()->str().c_str(), fi->param(i)->id()->str().size()); 711 if (allArgs.find(param) == allArgs.end()) { 712 std::cerr << "Warning: parameter " << *fi->param(i)->id() 713 << " not documented for function " << fi->id() << " at location " << fi->loc() 714 << "\n"; 715 } 716 } 717 718 std::string sig; 719 { 720 GCLock lock; 721 std::vector<VarDecl*> params(fi->paramCount()); 722 for (int i = 0; i < fi->paramCount(); i++) { 723 params[i] = fi->param(i); 724 } 725 auto* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), params); 726 std::ostringstream oss_sig; 727 oss_sig << *fi_c; 728 sig = oss_sig.str(); 729 sig.resize(sig.size() - 2); 730 } 731 732 std::ostringstream os; 733 os << "<div class='mzn-fundecl' id='" << HtmlDocOutput::make_html_id(sig) << "'>\n"; 734 os << "<div class='mzn-fundecl-code'>"; 735 os << "<a href='javascript:void(0)' onclick='revealMore(this)' " 736 "class='mzn-fundecl-more'>&#9664;</a>"; 737 738 std::ostringstream fs; 739 if (fi->ti()->type() == Type::ann()) { 740 fs << "annotation "; 741 os << "<span class='mzn-kw'>annotation</span> "; 742 } else if (fi->ti()->type() == Type::parbool()) { 743 fs << "test "; 744 os << "<span class='mzn-kw'>test</span> "; 745 } else if (fi->ti()->type() == Type::varbool()) { 746 fs << "predicate "; 747 os << "<span class='mzn-kw'>predicate</span> "; 748 } else { 749 fs << "function " << *fi->ti() << ": "; 750 os << "<span class='mzn-kw'>function</span> <span class='mzn-ti'>" << *fi->ti() 751 << "</span>: "; 752 } 753 fs << fi->id() << "("; 754 os << "<span class='mzn-fn-id'>" << fi->id() << "</span>("; 755 size_t align = fs.str().size(); 756 for (unsigned int i = 0; i < fi->paramCount(); i++) { 757 fs << *fi->param(i)->ti() << ": " << *fi->param(i)->id(); 758 if (i < fi->paramCount() - 1) { 759 fs << ", "; 760 } 761 } 762 bool splitArgs = (fs.str().size() > 70); 763 for (unsigned int i = 0; i < fi->paramCount(); i++) { 764 os << "<span class='mzn-ti'>" << *fi->param(i)->ti() << "</span>: " 765 << "<span class='mzn-id'>" << *fi->param(i)->id() << "</span>"; 766 if (i < fi->paramCount() - 1) { 767 os << ","; 768 if (splitArgs) { 769 os << "\n"; 770 for (auto j = static_cast<unsigned int>(align); (j--) != 0U;) { 771 os << " "; 772 } 773 } else { 774 os << " "; 775 } 776 } 777 } 778 os << ")"; 779 780 if (fi->e() != nullptr) { 781 FunctionI* f_body = fi; 782 bool alias; 783 do { 784 alias = false; 785 Call* c = Expression::dynamicCast<Call>(f_body->e()); 786 if ((c != nullptr) && c->argCount() == f_body->paramCount()) { 787 bool sameParams = true; 788 for (unsigned int i = 0; i < f_body->paramCount(); i++) { 789 Id* ident = c->arg(i)->dynamicCast<Id>(); 790 if (ident == nullptr || ident->decl() != f_body->param(i) || 791 ident->str() != c->decl()->param(i)->id()->str()) { 792 sameParams = false; 793 break; 794 } 795 } 796 if (sameParams) { 797 alias = true; 798 f_body = c->decl(); 799 } 800 } 801 } while (alias); 802 if (f_body->e() != nullptr) { 803 std::ostringstream body_os; 804 Printer p(body_os, 70); 805 p.print(f_body->e()); 806 807 std::string filename = 808 std::string(f_body->loc().filename().c_str(), f_body->loc().filename().size()); 809 size_t lastSlash = filename.find_last_of('/'); 810 if (lastSlash != std::string::npos) { 811 filename = filename.substr(lastSlash + 1, std::string::npos); 812 } 813 os << "<span class='mzn-fundecl-equals'> =</span>"; 814 os << "\n<div class='mzn-fundecl-more-code'>"; 815 os << "<div class='mzn-fundecl-body'>"; 816 os << body_os.str(); 817 os << "</div>\n"; 818 os << "(standard decomposition from " << filename << ":" << f_body->loc().firstLine() 819 << ")"; 820 os << "</div>"; 821 } 822 } 823 824 os << "</div>\n<div class='mzn-fundecl-doc'>\n"; 825 826 if (fi->id().c_str()[0] == '\'') { 827 std::string op = fi->id().substr(1, fi->id().size() - 2); 828 ; 829 const char* space = (op[0] >= 'a' ? " " : ""); 830 if (fi->paramCount() == 2) { 831 os << "<p>Usage: <span class=\"mzn-arg\">" << *fi->param(0)->id() << space << op << space 832 << *fi->param(1)->id() << "</span></p>"; 833 } else if (fi->paramCount() == 1) { 834 os << "<p>Usage: <span class=\"mzn-arg\">" << op << space << *fi->param(0)->id() 835 << "</span></p>"; 836 } 837 } 838 839 std::string dshtml = addHTML(ds); 840 841 os << dshtml; 842 if (!params.empty()) { 843 os << "<div class='mzn-fundecl-params-heading'>Parameters</div>\n"; 844 os << "<ul class='mzn-fundecl-params'>\n"; 845 for (auto& param : params) { 846 os << "<li><span class='mzn-arg'>" << param.first << "</span>: " << param.second 847 << "</li>\n"; 848 } 849 os << "</ul>\n"; 850 } 851 os << "</div>"; 852 os << "</div>"; 853 854 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, 855 std::string(fi->id().c_str(), fi->id().size()), sig, os.str()); 856 HtmlDocOutput::add_to_group(_maingroup, group, di); 857 } 858 } 859}; 860 861std::vector<HtmlDocument> HtmlPrinter::printHtml(EnvI& env, MiniZinc::Model* m, 862 const std::string& basename, int splitLevel, 863 bool includeStdLib, bool generateIndex) { 864 using namespace HtmlDocOutput; 865 Group g(basename, basename); 866 FunMap funMap; 867 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 868 iter_items(fv, m); 869 PrintHtmlVisitor phv(env, g, funMap, includeStdLib); 870 iter_items(phv, m); 871 872 std::vector<HtmlDocument> ret; 873 874 struct SI { 875 Group* g; 876 Group* p; 877 int level; 878 int idx; 879 SI(Group* g0, Group* p0, int level0, int idx0) : g(g0), p(p0), level(level0), idx(idx0) {} 880 }; 881 882 struct IndexEntry { 883 std::string id; 884 std::string sig; 885 std::string link; 886 std::string groupName; 887 IndexEntry(std::string id0, std::string sig0, std::string link0, std::string groupName0) 888 : id(std::move(id0)), 889 sig(std::move(sig0)), 890 link(std::move(link0)), 891 groupName(std::move(groupName0)) { 892 size_t spacepos = id.find_last_of(' '); 893 if (spacepos != std::string::npos) { 894 id = id.substr(spacepos + 1); 895 } 896 } 897 bool operator<(const IndexEntry& e) const { 898 if ((isalpha(id[0]) == 0) && (isalpha(e.id[0]) != 0)) { 899 return true; 900 } 901 return id == e.id ? groupName < e.groupName : id < e.id; 902 } 903 }; 904 std::vector<IndexEntry> index; 905 906 std::vector<SI> stack; 907 stack.emplace_back(&g, nullptr, 0, 0); 908 while (!stack.empty()) { 909 Group& g = *stack.back().g; 910 int curLevel = stack.back().level; 911 int curIdx = stack.back().idx; 912 Group* p = stack.back().p; 913 stack.pop_back(); 914 for (const auto& it : g.items) { 915 index.emplace_back(it.id, it.sig, g.fullPath, g.htmlName); 916 } 917 ret.emplace_back(g.fullPath, g.htmlName, 918 g.toHTML(curLevel, splitLevel, p, curIdx, basename, generateIndex)); 919 if (curLevel < splitLevel) { 920 for (unsigned int i = 0; i < g.subgroups.m.size(); i++) { 921 stack.emplace_back(g.subgroups.m[i], &g, curLevel + 1, i); 922 } 923 } 924 } 925 926 if (generateIndex) { 927 std::sort(index.begin(), index.end()); 928 std::ostringstream oss; 929 index.emplace_back("", "", "", ""); 930 931 std::vector<std::string> idxSections; 932 933 if (!index.empty()) { 934 if (isalpha(index[0].id[0]) != 0) { 935 char idxSec_c = (char)toupper(index[0].id[0]); 936 std::string idxSec(&idxSec_c, 1); 937 oss << "<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3>\n"; 938 idxSections.push_back(idxSec); 939 } else { 940 oss << "<h3 id='IdxSymbols'>Symbols</h3>\n"; 941 idxSections.emplace_back("Symbols"); 942 } 943 } 944 oss << "<ul>\n"; 945 std::string prevId = index.empty() ? "" : index[0].id; 946 std::vector<IndexEntry> curEntries; 947 for (auto ie : index) { 948 if (ie.id != prevId) { 949 oss << "<li>"; 950 assert(!curEntries.empty()); 951 IndexEntry& cur = curEntries[0]; 952 if (curEntries.size() == 1) { 953 oss << cur.id << " <a href='" << cur.link << ".html#" 954 << HtmlDocOutput::make_html_id(cur.sig) << "'>" 955 << "(" << cur.groupName << ")</a>"; 956 } else { 957 oss << cur.id << " ("; 958 bool first = true; 959 for (const auto& i_ie : curEntries) { 960 if (first) { 961 first = false; 962 } else { 963 oss << ", "; 964 } 965 oss << "<a href='" << i_ie.link << ".html#" << HtmlDocOutput::make_html_id(i_ie.sig) 966 << "'>"; 967 oss << i_ie.groupName << "</a>"; 968 } 969 oss << ")"; 970 } 971 oss << "</li>\n"; 972 curEntries.clear(); 973 } 974 if ((isalpha(ie.id[0]) != 0) && ie.id[0] != prevId[0]) { 975 char idxSec_c = (char)toupper(ie.id[0]); 976 std::string idxSec(&idxSec_c, 1); 977 oss << "</ul>\n<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3><ul>"; 978 idxSections.push_back(idxSec); 979 } 980 prevId = ie.id; 981 if (curEntries.empty() || curEntries.back().groupName != ie.groupName) { 982 curEntries.push_back(ie); 983 } 984 } 985 oss << "</ul>\n"; 986 987 std::ostringstream oss_header; 988 oss_header << "<div class='mzn-group-level-0'>\n"; 989 oss_header << "<div class='mzn-group-nav'>"; 990 oss_header << "<a class='mzn-nav-up' href='" << g.getAnchor(0, 1) << "' title='" << g.htmlName 991 << "'>&#8679;</a> "; 992 bool first = true; 993 for (const auto& is : idxSections) { 994 if (first) { 995 first = false; 996 } else { 997 oss_header << " | "; 998 } 999 oss_header << "<a href='#Idx" << is << "'>" << is << "</a>"; 1000 } 1001 1002 oss_header << "</div>"; 1003 1004 oss_header << "<div class='mzn-group-name'>Index</div>\n"; 1005 1006 HtmlDocument idx("doc-index", "Index", oss_header.str() + oss.str()); 1007 ret.push_back(idx); 1008 } 1009 return ret; 1010} 1011 1012class PrintRSTVisitor : public ItemVisitor { 1013protected: 1014 EnvI& _env; 1015 HtmlDocOutput::Group& _maingroup; 1016 HtmlDocOutput::FunMap& _funmap; 1017 bool _includeStdLib; 1018 1019 static std::vector<std::string> replaceArgsRST(std::string& s) { 1020 std::vector<std::string> replacements; 1021 std::ostringstream oss; 1022 size_t lastpos = 0; 1023 size_t pos = std::min(s.find("\\a"), s.find("\\p")); 1024 size_t mathjax_open = s.find("\\("); 1025 size_t mathjax_close = s.rfind("\\)"); 1026 while (pos != std::string::npos) { 1027 oss << s.substr(lastpos, pos - lastpos); 1028 size_t start = pos; 1029 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 1030 start++; 1031 } 1032 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 1033 start++; 1034 } 1035 size_t end = start + 1; 1036 while (end < s.size() && ((isalnum(s[end]) != 0) || s[end] == '_')) { 1037 end++; 1038 } 1039 bool needSpace = pos != 0 && s[pos - 1] != ' ' && s[pos - 1] != '\n'; 1040 if (s[pos + 1] == 'a') { 1041 replacements.push_back(s.substr(start, end - start)); 1042 if (pos >= mathjax_open && pos <= mathjax_close) { 1043 oss << "{\\bf " << replacements.back() << "}"; 1044 } else { 1045 oss << (needSpace ? " " : "") << "``" << replacements.back() << "`` "; 1046 } 1047 } else { 1048 if (pos >= mathjax_open && pos <= mathjax_close) { 1049 oss << "{\\bf " << s.substr(start, end - start) << "}"; 1050 } else { 1051 oss << (needSpace ? " " : "") << "``" << s.substr(start, end - start) << "`` "; 1052 } 1053 } 1054 lastpos = end; 1055 pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); 1056 } 1057 oss << s.substr(lastpos, std::string::npos); 1058 s = oss.str(); 1059 1060 std::ostringstream oss2; 1061 pos = std::min(s.find("\\("), s.find("\\)")); 1062 lastpos = 0; 1063 while (pos != std::string::npos) { 1064 if (s[pos + 1] == ')') { 1065 // remove trailing whitespace 1066 std::string t = s.substr(lastpos, pos - lastpos); 1067 size_t t_end = t.find_last_not_of(' '); 1068 if (t_end != std::string::npos) { 1069 t_end++; 1070 } 1071 oss2 << t.substr(0, t_end); 1072 } else { 1073 oss2 << s.substr(lastpos, pos - lastpos); 1074 } 1075 lastpos = pos + 2; 1076 if (s[pos + 1] == '(') { 1077 oss2 << ":math:`"; 1078 lastpos = s.find_first_not_of(' ', lastpos); 1079 } else { 1080 oss2 << "`"; 1081 } 1082 pos = std::min(s.find("\\(", lastpos), s.find("\\)", lastpos)); 1083 } 1084 oss2 << s.substr(lastpos, std::string::npos); 1085 s = oss2.str(); 1086 1087 std::ostringstream oss3; 1088 pos = std::min(s.find("\\["), s.find("\\]")); 1089 lastpos = 0; 1090 while (pos != std::string::npos) { 1091 if (s[pos + 1] == ']') { 1092 // remove trailing whitespace 1093 std::string t = s.substr(lastpos, pos - lastpos); 1094 size_t t_end = t.find_last_not_of(' '); 1095 if (t_end != std::string::npos) { 1096 t_end++; 1097 } 1098 oss3 << t.substr(0, t_end); 1099 } else { 1100 oss3 << s.substr(lastpos, pos - lastpos); 1101 } 1102 lastpos = pos + 2; 1103 if (s[pos + 1] == '[') { 1104 oss3 << "``"; 1105 lastpos = s.find_first_not_of(' ', lastpos); 1106 } else { 1107 oss3 << "``"; 1108 } 1109 pos = std::min(s.find("\\[", lastpos), s.find("\\]", lastpos)); 1110 } 1111 oss3 << s.substr(lastpos, std::string::npos); 1112 s = oss3.str(); 1113 return replacements; 1114 } 1115 1116 static std::pair<std::string, std::string> extractArgLine(std::string& s, size_t n) { 1117 size_t start = n; 1118 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 1119 start++; 1120 } 1121 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 1122 start++; 1123 } 1124 size_t end = start + 1; 1125 while (end < s.size() && s[end] != ':') { 1126 end++; 1127 } 1128 std::string arg = s.substr(start, end - start); 1129 size_t doc_start = end + 1; 1130 while (end < s.size() && s[end] != '\n') { 1131 end++; 1132 } 1133 std::string ret = s.substr(doc_start, end - doc_start); 1134 replaceArgsRST(ret); 1135 s = s.substr(0, n) + s.substr(end, std::string::npos); 1136 return make_pair(arg, ret); 1137 } 1138 1139public: 1140 PrintRSTVisitor(EnvI& env, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, 1141 bool includeStdLib) 1142 : _env(env), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} 1143 bool enterModel(Model* m) { 1144 if (!_includeStdLib && m->filename() == "stdlib.mzn") { 1145 return false; 1146 } 1147 const std::string& dc = m->docComment(); 1148 if (!dc.empty()) { 1149 size_t gpos = dc.find("@groupdef"); 1150 while (gpos != std::string::npos) { 1151 size_t start = gpos; 1152 while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') { 1153 start++; 1154 } 1155 while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) { 1156 start++; 1157 } 1158 size_t end = start + 1; 1159 while (end < dc.size() && ((isalnum(dc[end]) != 0) || dc[end] == '_' || dc[end] == '.')) { 1160 end++; 1161 } 1162 std::string groupName = dc.substr(start, end - start); 1163 size_t doc_start = end + 1; 1164 while (end < dc.size() && dc[end] != '\n') { 1165 end++; 1166 } 1167 std::string groupHTMLName = dc.substr(doc_start, end - doc_start); 1168 1169 size_t next = dc.find("@groupdef", gpos + 1); 1170 std::string groupDesc = dc.substr(end, next == std::string::npos ? next : next - end); 1171 replaceArgsRST(groupDesc); 1172 HtmlDocOutput::set_group_desc(_maingroup, groupName, groupHTMLName, groupDesc); 1173 gpos = next; 1174 } 1175 } 1176 return true; 1177 } 1178 /// Visit variable declaration 1179 void vVarDeclI(VarDeclI* vdi) { 1180 if (Call* docstring = Expression::dynamicCast<Call>( 1181 get_annotation(vdi->e()->ann(), constants().ann.doc_comment))) { 1182 std::string ds = eval_string(_env, docstring->arg(0)); 1183 std::string group("main"); 1184 size_t group_idx = ds.find("@group"); 1185 if (group_idx != std::string::npos) { 1186 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 1187 } 1188 std::ostringstream os; 1189 std::string sig = vdi->e()->type().toString(_env) + " " + 1190 std::string(vdi->e()->id()->str().c_str(), vdi->e()->id()->str().size()); 1191 1192 std::string myMainGroup = group.substr(0, group.find_first_of('.')); 1193 auto it = _maingroup.subgroups.find(myMainGroup); 1194 os << ".. index::\n"; 1195 if (it != _maingroup.subgroups.m.end()) { 1196 os << " pair: " << (*it)->htmlName << "; " << *vdi->e()->id() << "\n\n"; 1197 } else { 1198 std::cerr << "did not find " << myMainGroup << "\n"; 1199 os << " single: " << *vdi->e()->id() << "\n\n"; 1200 } 1201 1202 os << ".. code-block:: minizinc\n\n"; 1203 if (vdi->e()->ti()->type() == Type::ann()) { 1204 os << " annotation " << *vdi->e()->id(); 1205 } else { 1206 os << " " << *vdi->e()->ti() << ": " << *vdi->e()->id(); 1207 } 1208 os << "\n\n"; 1209 os << HtmlDocOutput::trim(ds) << "\n\n"; 1210 GCLock lock; 1211 HtmlDocOutput::DocItem di( 1212 vdi->e()->type().isPar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, 1213 sig, sig, os.str()); 1214 HtmlDocOutput::add_to_group(_maingroup, group, di); 1215 } 1216 } 1217 /// Visit function item 1218 void vFunctionI(FunctionI* fi) { 1219 if (Call* docstring = 1220 Expression::dynamicCast<Call>(get_annotation(fi->ann(), constants().ann.doc_comment))) { 1221 std::string ds = eval_string(_env, docstring->arg(0)); 1222 std::string group("main"); 1223 size_t group_idx = ds.find("@group"); 1224 if (group_idx != std::string::npos) { 1225 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 1226 } 1227 1228 size_t param_idx = ds.find("@param"); 1229 std::vector<std::pair<std::string, std::string> > params; 1230 while (param_idx != std::string::npos) { 1231 params.push_back(extractArgLine(ds, param_idx)); 1232 param_idx = ds.find("@param"); 1233 } 1234 1235 std::vector<std::string> args = replaceArgsRST(ds); 1236 1237 std::unordered_set<std::string> allArgs; 1238 for (auto& arg : args) { 1239 allArgs.insert(arg); 1240 } 1241 for (auto& param : params) { 1242 allArgs.insert(param.first); 1243 } 1244 1245 GCLock lock; 1246 for (unsigned int i = 0; i < fi->paramCount(); i++) { 1247 if (allArgs.find(std::string(fi->param(i)->id()->str().c_str(), 1248 fi->param(i)->id()->str().size())) == allArgs.end()) { 1249 std::cerr << "Warning: parameter " << *fi->param(i)->id() 1250 << " not documented for function " << fi->id() << " at location " << fi->loc() 1251 << "\n"; 1252 } 1253 } 1254 1255 std::string sig; 1256 { 1257 GCLock lock; 1258 std::vector<VarDecl*> params(fi->paramCount()); 1259 for (int i = 0; i < fi->paramCount(); i++) { 1260 params[i] = fi->param(i); 1261 } 1262 auto* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), params); 1263 std::ostringstream oss_sig; 1264 oss_sig << *fi_c; 1265 sig = oss_sig.str(); 1266 sig.resize(sig.size() - 2); 1267 } 1268 1269 std::ostringstream os; 1270 std::ostringstream fs; 1271 std::string myMainGroup = group.substr(0, group.find_first_of('.')); 1272 auto it = _maingroup.subgroups.find(myMainGroup); 1273 os << ".. index::\n"; 1274 if (it != _maingroup.subgroups.m.end()) { 1275 os << " pair: " << (*it)->htmlName << "; " << fi->id() << "\n\n"; 1276 } else { 1277 std::cerr << "did not find " << myMainGroup << "\n"; 1278 os << " single: " << fi->id() << "\n\n"; 1279 } 1280 os << ".. code-block:: minizinc\n\n"; 1281 1282 if (fi->ti()->type() == Type::ann()) { 1283 fs << "annotation "; 1284 } else if (fi->ti()->type() == Type::parbool()) { 1285 fs << "test "; 1286 } else if (fi->ti()->type() == Type::varbool()) { 1287 fs << "predicate "; 1288 } else { 1289 fs << "function " << *fi->ti() << ": "; 1290 } 1291 fs << fi->id() << "("; 1292 os << " " << fs.str(); 1293 size_t align = fs.str().size(); 1294 for (unsigned int i = 0; i < fi->paramCount(); i++) { 1295 fs << *fi->param(i)->ti(); 1296 std::ostringstream fid; 1297 fid << *fi->param(i)->id(); 1298 if (!fid.str().empty()) { 1299 fs << ": " << *fi->param(i)->id(); 1300 } 1301 if (i < fi->paramCount() - 1) { 1302 fs << ", "; 1303 } 1304 } 1305 bool splitArgs = (fs.str().size() > 70); 1306 for (unsigned int i = 0; i < fi->paramCount(); i++) { 1307 os << *fi->param(i)->ti(); 1308 std::ostringstream fid; 1309 fid << *fi->param(i)->id(); 1310 if (!fid.str().empty()) { 1311 os << ": " << *fi->param(i)->id(); 1312 } 1313 if (i < fi->paramCount() - 1) { 1314 os << ","; 1315 if (splitArgs) { 1316 os << "\n "; 1317 for (auto j = static_cast<unsigned int>(align); (j--) != 0U;) { 1318 os << " "; 1319 } 1320 } else { 1321 os << " "; 1322 } 1323 } 1324 } 1325 os << ")"; 1326 1327 os << "\n\n"; 1328 1329 if (fi->id().c_str()[0] == '\'') { 1330 std::string op = fi->id().substr(1, fi->id().size() - 2); 1331 if (fi->paramCount() == 2) { 1332 os << "Usage: ``" << *fi->param(0)->id() << " " << op << " " << *fi->param(1)->id() 1333 << "``\n\n"; 1334 } else if (fi->paramCount() == 1) { 1335 os << "Usage: ``" << op << " " << *fi->param(0)->id() << "``\n\n"; 1336 } 1337 } 1338 1339 os << HtmlDocOutput::trim(ds) << "\n\n"; 1340 1341 if (fi->e() != nullptr) { 1342 FunctionI* f_body = fi; 1343 bool alias; 1344 do { 1345 alias = false; 1346 Call* c = Expression::dynamicCast<Call>(f_body->e()); 1347 if ((c != nullptr) && c->argCount() == f_body->paramCount()) { 1348 bool sameParams = true; 1349 for (unsigned int i = 0; i < f_body->paramCount(); i++) { 1350 Id* ident = c->arg(i)->dynamicCast<Id>(); 1351 if (ident == nullptr || ident->decl() != f_body->param(i) || 1352 ident->str() != c->decl()->param(i)->id()->str()) { 1353 sameParams = false; 1354 break; 1355 } 1356 } 1357 if (sameParams) { 1358 alias = true; 1359 f_body = c->decl(); 1360 } 1361 } 1362 } while (alias); 1363 if (f_body->e() != nullptr) { 1364 std::string filename = 1365 std::string(f_body->loc().filename().c_str(), f_body->loc().filename().size()); 1366 size_t filePos = filename.find("std/"); 1367 if (filePos != std::string::npos) { 1368 filePos += 4; 1369 os << ".. only:: builder_html\n\n"; 1370 os << " `More... <https://github.com/MiniZinc/libminizinc/blob/" << MZN_VERSION_MAJOR 1371 << "." << MZN_VERSION_MINOR << "." << MZN_VERSION_PATCH << "/share/minizinc/std/" 1372 << filename.substr(filePos, std::string::npos) << "#L" << f_body->loc().firstLine() 1373 << "-L" << f_body->loc().lastLine() << ">`__\n\n"; 1374 } 1375 } 1376 } 1377 1378 if (!params.empty()) { 1379 os << "Parameters:\n\n"; 1380 for (auto& param : params) { 1381 os << "- ``" << param.first << "``: " << param.second << "\n"; 1382 } 1383 os << "\n"; 1384 } 1385 os << "\n"; 1386 1387 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, 1388 std::string(fi->id().c_str(), fi->id().size()), sig, os.str()); 1389 HtmlDocOutput::add_to_group(_maingroup, group, di); 1390 } 1391 } 1392}; 1393 1394std::vector<HtmlDocument> RSTPrinter::printRST(EnvI& env, MiniZinc::Model* m, 1395 const std::string& basename, int splitLevel, 1396 bool includeStdLib, bool generateIndex) { 1397 using namespace HtmlDocOutput; 1398 Group g(basename, basename); 1399 FunMap funMap; 1400 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 1401 iter_items(fv, m); 1402 PrintRSTVisitor prv(env, g, funMap, includeStdLib); 1403 iter_items(prv, m); 1404 1405 std::vector<HtmlDocument> ret; 1406 1407 std::ostringstream oss; 1408 oss << Group::rstHeading(g.htmlName, 0); 1409 oss << trim(g.desc) << "\n"; 1410 oss << ".. toctree::\n\n"; 1411 for (auto* sg : g.subgroups.m) { 1412 oss << " " << sg->fullPath << "\n"; 1413 } 1414 1415 ret.emplace_back(g.fullPath, g.htmlName, oss.str()); 1416 1417 for (auto& sg : g.subgroups.m) { 1418 ret.emplace_back(sg->fullPath, sg->htmlName, sg->toRST(0)); 1419 } 1420 return ret; 1421} 1422 1423} // namespace MiniZinc