this repo has no description
at develop 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->params().size(); i++) { 710 std::string param(fi->params()[i]->id()->str().c_str(), 711 fi->params()[i]->id()->str().size()); 712 if (allArgs.find(param) == allArgs.end()) { 713 std::cerr << "Warning: parameter " << *fi->params()[i]->id() 714 << " not documented for function " << fi->id() << " at location " << fi->loc() 715 << "\n"; 716 } 717 } 718 719 std::string sig; 720 { 721 GCLock lock; 722 auto* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); 723 std::ostringstream oss_sig; 724 oss_sig << *fi_c; 725 sig = oss_sig.str(); 726 sig.resize(sig.size() - 2); 727 } 728 729 std::ostringstream os; 730 os << "<div class='mzn-fundecl' id='" << HtmlDocOutput::make_html_id(sig) << "'>\n"; 731 os << "<div class='mzn-fundecl-code'>"; 732 os << "<a href='javascript:void(0)' onclick='revealMore(this)' " 733 "class='mzn-fundecl-more'>&#9664;</a>"; 734 735 std::ostringstream fs; 736 if (fi->ti()->type() == Type::ann()) { 737 fs << "annotation "; 738 os << "<span class='mzn-kw'>annotation</span> "; 739 } else if (fi->ti()->type() == Type::parbool()) { 740 fs << "test "; 741 os << "<span class='mzn-kw'>test</span> "; 742 } else if (fi->ti()->type() == Type::varbool()) { 743 fs << "predicate "; 744 os << "<span class='mzn-kw'>predicate</span> "; 745 } else { 746 fs << "function " << *fi->ti() << ": "; 747 os << "<span class='mzn-kw'>function</span> <span class='mzn-ti'>" << *fi->ti() 748 << "</span>: "; 749 } 750 fs << fi->id() << "("; 751 os << "<span class='mzn-fn-id'>" << fi->id() << "</span>("; 752 size_t align = fs.str().size(); 753 for (unsigned int i = 0; i < fi->params().size(); i++) { 754 fs << *fi->params()[i]->ti() << ": " << *fi->params()[i]->id(); 755 if (i < fi->params().size() - 1) { 756 fs << ", "; 757 } 758 } 759 bool splitArgs = (fs.str().size() > 70); 760 for (unsigned int i = 0; i < fi->params().size(); i++) { 761 os << "<span class='mzn-ti'>" << *fi->params()[i]->ti() << "</span>: " 762 << "<span class='mzn-id'>" << *fi->params()[i]->id() << "</span>"; 763 if (i < fi->params().size() - 1) { 764 os << ","; 765 if (splitArgs) { 766 os << "\n"; 767 for (auto j = static_cast<unsigned int>(align); (j--) != 0U;) { 768 os << " "; 769 } 770 } else { 771 os << " "; 772 } 773 } 774 } 775 os << ")"; 776 777 if (fi->e() != nullptr) { 778 FunctionI* f_body = fi; 779 bool alias; 780 do { 781 alias = false; 782 Call* c = Expression::dynamicCast<Call>(f_body->e()); 783 if ((c != nullptr) && c->argCount() == f_body->params().size()) { 784 bool sameParams = true; 785 for (unsigned int i = 0; i < f_body->params().size(); i++) { 786 Id* ident = c->arg(i)->dynamicCast<Id>(); 787 if (ident == nullptr || ident->decl() != f_body->params()[i] || 788 ident->str() != c->decl()->params()[i]->id()->str()) { 789 sameParams = false; 790 break; 791 } 792 } 793 if (sameParams) { 794 alias = true; 795 f_body = c->decl(); 796 } 797 } 798 } while (alias); 799 if (f_body->e() != nullptr) { 800 std::ostringstream body_os; 801 Printer p(body_os, 70); 802 p.print(f_body->e()); 803 804 std::string filename = 805 std::string(f_body->loc().filename().c_str(), f_body->loc().filename().size()); 806 size_t lastSlash = filename.find_last_of('/'); 807 if (lastSlash != std::string::npos) { 808 filename = filename.substr(lastSlash + 1, std::string::npos); 809 } 810 os << "<span class='mzn-fundecl-equals'> =</span>"; 811 os << "\n<div class='mzn-fundecl-more-code'>"; 812 os << "<div class='mzn-fundecl-body'>"; 813 os << body_os.str(); 814 os << "</div>\n"; 815 os << "(standard decomposition from " << filename << ":" << f_body->loc().firstLine() 816 << ")"; 817 os << "</div>"; 818 } 819 } 820 821 os << "</div>\n<div class='mzn-fundecl-doc'>\n"; 822 823 if (fi->id().c_str()[0] == '\'') { 824 std::string op = fi->id().substr(1, fi->id().size() - 2); 825 ; 826 const char* space = (op[0] >= 'a' ? " " : ""); 827 if (fi->params().size() == 2) { 828 os << "<p>Usage: <span class=\"mzn-arg\">" << *fi->params()[0]->id() << space << op 829 << space << *fi->params()[1]->id() << "</span></p>"; 830 } else if (fi->params().size() == 1) { 831 os << "<p>Usage: <span class=\"mzn-arg\">" << op << space << *fi->params()[0]->id() 832 << "</span></p>"; 833 } 834 } 835 836 std::string dshtml = addHTML(ds); 837 838 os << dshtml; 839 if (!params.empty()) { 840 os << "<div class='mzn-fundecl-params-heading'>Parameters</div>\n"; 841 os << "<ul class='mzn-fundecl-params'>\n"; 842 for (auto& param : params) { 843 os << "<li><span class='mzn-arg'>" << param.first << "</span>: " << param.second 844 << "</li>\n"; 845 } 846 os << "</ul>\n"; 847 } 848 os << "</div>"; 849 os << "</div>"; 850 851 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, 852 std::string(fi->id().c_str(), fi->id().size()), sig, os.str()); 853 HtmlDocOutput::add_to_group(_maingroup, group, di); 854 } 855 } 856}; 857 858std::vector<HtmlDocument> HtmlPrinter::printHtml(EnvI& env, MiniZinc::Model* m, 859 const std::string& basename, int splitLevel, 860 bool includeStdLib, bool generateIndex) { 861 using namespace HtmlDocOutput; 862 Group g(basename, basename); 863 FunMap funMap; 864 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 865 iter_items(fv, m); 866 PrintHtmlVisitor phv(env, g, funMap, includeStdLib); 867 iter_items(phv, m); 868 869 std::vector<HtmlDocument> ret; 870 871 struct SI { 872 Group* g; 873 Group* p; 874 int level; 875 int idx; 876 SI(Group* g0, Group* p0, int level0, int idx0) : g(g0), p(p0), level(level0), idx(idx0) {} 877 }; 878 879 struct IndexEntry { 880 std::string id; 881 std::string sig; 882 std::string link; 883 std::string groupName; 884 IndexEntry(std::string id0, std::string sig0, std::string link0, std::string groupName0) 885 : id(std::move(id0)), 886 sig(std::move(sig0)), 887 link(std::move(link0)), 888 groupName(std::move(groupName0)) { 889 size_t spacepos = id.find_last_of(' '); 890 if (spacepos != std::string::npos) { 891 id = id.substr(spacepos + 1); 892 } 893 } 894 bool operator<(const IndexEntry& e) const { 895 if ((isalpha(id[0]) == 0) && (isalpha(e.id[0]) != 0)) { 896 return true; 897 } 898 return id == e.id ? groupName < e.groupName : id < e.id; 899 } 900 }; 901 std::vector<IndexEntry> index; 902 903 std::vector<SI> stack; 904 stack.emplace_back(&g, nullptr, 0, 0); 905 while (!stack.empty()) { 906 Group& g = *stack.back().g; 907 int curLevel = stack.back().level; 908 int curIdx = stack.back().idx; 909 Group* p = stack.back().p; 910 stack.pop_back(); 911 for (const auto& it : g.items) { 912 index.emplace_back(it.id, it.sig, g.fullPath, g.htmlName); 913 } 914 ret.emplace_back(g.fullPath, g.htmlName, 915 g.toHTML(curLevel, splitLevel, p, curIdx, basename, generateIndex)); 916 if (curLevel < splitLevel) { 917 for (unsigned int i = 0; i < g.subgroups.m.size(); i++) { 918 stack.emplace_back(g.subgroups.m[i], &g, curLevel + 1, i); 919 } 920 } 921 } 922 923 if (generateIndex) { 924 std::sort(index.begin(), index.end()); 925 std::ostringstream oss; 926 index.emplace_back("", "", "", ""); 927 928 std::vector<std::string> idxSections; 929 930 if (!index.empty()) { 931 if (isalpha(index[0].id[0]) != 0) { 932 char idxSec_c = (char)toupper(index[0].id[0]); 933 std::string idxSec(&idxSec_c, 1); 934 oss << "<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3>\n"; 935 idxSections.push_back(idxSec); 936 } else { 937 oss << "<h3 id='IdxSymbols'>Symbols</h3>\n"; 938 idxSections.emplace_back("Symbols"); 939 } 940 } 941 oss << "<ul>\n"; 942 std::string prevId = index.empty() ? "" : index[0].id; 943 std::vector<IndexEntry> curEntries; 944 for (auto ie : index) { 945 if (ie.id != prevId) { 946 oss << "<li>"; 947 assert(!curEntries.empty()); 948 IndexEntry& cur = curEntries[0]; 949 if (curEntries.size() == 1) { 950 oss << cur.id << " <a href='" << cur.link << ".html#" 951 << HtmlDocOutput::make_html_id(cur.sig) << "'>" 952 << "(" << cur.groupName << ")</a>"; 953 } else { 954 oss << cur.id << " ("; 955 bool first = true; 956 for (const auto& i_ie : curEntries) { 957 if (first) { 958 first = false; 959 } else { 960 oss << ", "; 961 } 962 oss << "<a href='" << i_ie.link << ".html#" << HtmlDocOutput::make_html_id(i_ie.sig) 963 << "'>"; 964 oss << i_ie.groupName << "</a>"; 965 } 966 oss << ")"; 967 } 968 oss << "</li>\n"; 969 curEntries.clear(); 970 } 971 if ((isalpha(ie.id[0]) != 0) && ie.id[0] != prevId[0]) { 972 char idxSec_c = (char)toupper(ie.id[0]); 973 std::string idxSec(&idxSec_c, 1); 974 oss << "</ul>\n<h3 id='Idx" << idxSec << "'>" << idxSec << "</h3><ul>"; 975 idxSections.push_back(idxSec); 976 } 977 prevId = ie.id; 978 if (curEntries.empty() || curEntries.back().groupName != ie.groupName) { 979 curEntries.push_back(ie); 980 } 981 } 982 oss << "</ul>\n"; 983 984 std::ostringstream oss_header; 985 oss_header << "<div class='mzn-group-level-0'>\n"; 986 oss_header << "<div class='mzn-group-nav'>"; 987 oss_header << "<a class='mzn-nav-up' href='" << g.getAnchor(0, 1) << "' title='" << g.htmlName 988 << "'>&#8679;</a> "; 989 bool first = true; 990 for (const auto& is : idxSections) { 991 if (first) { 992 first = false; 993 } else { 994 oss_header << " | "; 995 } 996 oss_header << "<a href='#Idx" << is << "'>" << is << "</a>"; 997 } 998 999 oss_header << "</div>"; 1000 1001 oss_header << "<div class='mzn-group-name'>Index</div>\n"; 1002 1003 HtmlDocument idx("doc-index", "Index", oss_header.str() + oss.str()); 1004 ret.push_back(idx); 1005 } 1006 return ret; 1007} 1008 1009class PrintRSTVisitor : public ItemVisitor { 1010protected: 1011 EnvI& _env; 1012 HtmlDocOutput::Group& _maingroup; 1013 HtmlDocOutput::FunMap& _funmap; 1014 bool _includeStdLib; 1015 1016 static std::vector<std::string> replaceArgsRST(std::string& s) { 1017 std::vector<std::string> replacements; 1018 std::ostringstream oss; 1019 size_t lastpos = 0; 1020 size_t pos = std::min(s.find("\\a"), s.find("\\p")); 1021 size_t mathjax_open = s.find("\\("); 1022 size_t mathjax_close = s.rfind("\\)"); 1023 while (pos != std::string::npos) { 1024 oss << s.substr(lastpos, pos - lastpos); 1025 size_t start = pos; 1026 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 1027 start++; 1028 } 1029 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 1030 start++; 1031 } 1032 size_t end = start + 1; 1033 while (end < s.size() && ((isalnum(s[end]) != 0) || s[end] == '_')) { 1034 end++; 1035 } 1036 bool needSpace = pos != 0 && s[pos - 1] != ' ' && s[pos - 1] != '\n'; 1037 if (s[pos + 1] == 'a') { 1038 replacements.push_back(s.substr(start, end - start)); 1039 if (pos >= mathjax_open && pos <= mathjax_close) { 1040 oss << "{\\bf " << replacements.back() << "}"; 1041 } else { 1042 oss << (needSpace ? " " : "") << "``" << replacements.back() << "`` "; 1043 } 1044 } else { 1045 if (pos >= mathjax_open && pos <= mathjax_close) { 1046 oss << "{\\bf " << s.substr(start, end - start) << "}"; 1047 } else { 1048 oss << (needSpace ? " " : "") << "``" << s.substr(start, end - start) << "`` "; 1049 } 1050 } 1051 lastpos = end; 1052 pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); 1053 } 1054 oss << s.substr(lastpos, std::string::npos); 1055 s = oss.str(); 1056 1057 std::ostringstream oss2; 1058 pos = std::min(s.find("\\("), s.find("\\)")); 1059 lastpos = 0; 1060 while (pos != std::string::npos) { 1061 if (s[pos + 1] == ')') { 1062 // remove trailing whitespace 1063 std::string t = s.substr(lastpos, pos - lastpos); 1064 size_t t_end = t.find_last_not_of(' '); 1065 if (t_end != std::string::npos) { 1066 t_end++; 1067 } 1068 oss2 << t.substr(0, t_end); 1069 } else { 1070 oss2 << s.substr(lastpos, pos - lastpos); 1071 } 1072 lastpos = pos + 2; 1073 if (s[pos + 1] == '(') { 1074 oss2 << ":math:`"; 1075 lastpos = s.find_first_not_of(' ', lastpos); 1076 } else { 1077 oss2 << "`"; 1078 } 1079 pos = std::min(s.find("\\(", lastpos), s.find("\\)", lastpos)); 1080 } 1081 oss2 << s.substr(lastpos, std::string::npos); 1082 s = oss2.str(); 1083 1084 std::ostringstream oss3; 1085 pos = std::min(s.find("\\["), s.find("\\]")); 1086 lastpos = 0; 1087 while (pos != std::string::npos) { 1088 if (s[pos + 1] == ']') { 1089 // remove trailing whitespace 1090 std::string t = s.substr(lastpos, pos - lastpos); 1091 size_t t_end = t.find_last_not_of(' '); 1092 if (t_end != std::string::npos) { 1093 t_end++; 1094 } 1095 oss3 << t.substr(0, t_end); 1096 } else { 1097 oss3 << s.substr(lastpos, pos - lastpos); 1098 } 1099 lastpos = pos + 2; 1100 if (s[pos + 1] == '[') { 1101 oss3 << "``"; 1102 lastpos = s.find_first_not_of(' ', lastpos); 1103 } else { 1104 oss3 << "``"; 1105 } 1106 pos = std::min(s.find("\\[", lastpos), s.find("\\]", lastpos)); 1107 } 1108 oss3 << s.substr(lastpos, std::string::npos); 1109 s = oss3.str(); 1110 return replacements; 1111 } 1112 1113 static std::pair<std::string, std::string> extractArgLine(std::string& s, size_t n) { 1114 size_t start = n; 1115 while (start < s.size() && s[start] != ' ' && s[start] != '\t') { 1116 start++; 1117 } 1118 while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) { 1119 start++; 1120 } 1121 size_t end = start + 1; 1122 while (end < s.size() && s[end] != ':') { 1123 end++; 1124 } 1125 std::string arg = s.substr(start, end - start); 1126 size_t doc_start = end + 1; 1127 while (end < s.size() && s[end] != '\n') { 1128 end++; 1129 } 1130 std::string ret = s.substr(doc_start, end - doc_start); 1131 replaceArgsRST(ret); 1132 s = s.substr(0, n) + s.substr(end, std::string::npos); 1133 return make_pair(arg, ret); 1134 } 1135 1136public: 1137 PrintRSTVisitor(EnvI& env, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, 1138 bool includeStdLib) 1139 : _env(env), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} 1140 bool enterModel(Model* m) { 1141 if (!_includeStdLib && m->filename() == "stdlib.mzn") { 1142 return false; 1143 } 1144 const std::string& dc = m->docComment(); 1145 if (!dc.empty()) { 1146 size_t gpos = dc.find("@groupdef"); 1147 while (gpos != std::string::npos) { 1148 size_t start = gpos; 1149 while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') { 1150 start++; 1151 } 1152 while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) { 1153 start++; 1154 } 1155 size_t end = start + 1; 1156 while (end < dc.size() && ((isalnum(dc[end]) != 0) || dc[end] == '_' || dc[end] == '.')) { 1157 end++; 1158 } 1159 std::string groupName = dc.substr(start, end - start); 1160 size_t doc_start = end + 1; 1161 while (end < dc.size() && dc[end] != '\n') { 1162 end++; 1163 } 1164 std::string groupHTMLName = dc.substr(doc_start, end - doc_start); 1165 1166 size_t next = dc.find("@groupdef", gpos + 1); 1167 std::string groupDesc = dc.substr(end, next == std::string::npos ? next : next - end); 1168 replaceArgsRST(groupDesc); 1169 HtmlDocOutput::set_group_desc(_maingroup, groupName, groupHTMLName, groupDesc); 1170 gpos = next; 1171 } 1172 } 1173 return true; 1174 } 1175 /// Visit variable declaration 1176 void vVarDeclI(VarDeclI* vdi) { 1177 if (Call* docstring = Expression::dynamicCast<Call>( 1178 get_annotation(vdi->e()->ann(), constants().ann.doc_comment))) { 1179 std::string ds = eval_string(_env, docstring->arg(0)); 1180 std::string group("main"); 1181 size_t group_idx = ds.find("@group"); 1182 if (group_idx != std::string::npos) { 1183 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 1184 } 1185 std::ostringstream os; 1186 std::string sig = vdi->e()->type().toString(_env) + " " + 1187 std::string(vdi->e()->id()->str().c_str(), vdi->e()->id()->str().size()); 1188 1189 std::string myMainGroup = group.substr(0, group.find_first_of('.')); 1190 auto it = _maingroup.subgroups.find(myMainGroup); 1191 os << ".. index::\n"; 1192 if (it != _maingroup.subgroups.m.end()) { 1193 os << " pair: " << (*it)->htmlName << "; " << *vdi->e()->id() << "\n\n"; 1194 } else { 1195 std::cerr << "did not find " << myMainGroup << "\n"; 1196 os << " single: " << *vdi->e()->id() << "\n\n"; 1197 } 1198 1199 os << ".. code-block:: minizinc\n\n"; 1200 if (vdi->e()->ti()->type() == Type::ann()) { 1201 os << " annotation " << *vdi->e()->id(); 1202 } else { 1203 os << " " << *vdi->e()->ti() << ": " << *vdi->e()->id(); 1204 } 1205 os << "\n\n"; 1206 os << HtmlDocOutput::trim(ds) << "\n\n"; 1207 GCLock lock; 1208 HtmlDocOutput::DocItem di( 1209 vdi->e()->type().isPar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, 1210 sig, sig, os.str()); 1211 HtmlDocOutput::add_to_group(_maingroup, group, di); 1212 } 1213 } 1214 /// Visit function item 1215 void vFunctionI(FunctionI* fi) { 1216 if (Call* docstring = 1217 Expression::dynamicCast<Call>(get_annotation(fi->ann(), constants().ann.doc_comment))) { 1218 std::string ds = eval_string(_env, docstring->arg(0)); 1219 std::string group("main"); 1220 size_t group_idx = ds.find("@group"); 1221 if (group_idx != std::string::npos) { 1222 group = HtmlDocOutput::extract_arg_word(ds, group_idx); 1223 } 1224 1225 size_t param_idx = ds.find("@param"); 1226 std::vector<std::pair<std::string, std::string> > params; 1227 while (param_idx != std::string::npos) { 1228 params.push_back(extractArgLine(ds, param_idx)); 1229 param_idx = ds.find("@param"); 1230 } 1231 1232 std::vector<std::string> args = replaceArgsRST(ds); 1233 1234 std::unordered_set<std::string> allArgs; 1235 for (auto& arg : args) { 1236 allArgs.insert(arg); 1237 } 1238 for (auto& param : params) { 1239 allArgs.insert(param.first); 1240 } 1241 1242 GCLock lock; 1243 for (unsigned int i = 0; i < fi->params().size(); i++) { 1244 if (allArgs.find(std::string(fi->params()[i]->id()->str().c_str(), 1245 fi->params()[i]->id()->str().size())) == allArgs.end()) { 1246 std::cerr << "Warning: parameter " << *fi->params()[i]->id() 1247 << " not documented for function " << fi->id() << " at location " << fi->loc() 1248 << "\n"; 1249 } 1250 } 1251 1252 std::string sig; 1253 { 1254 GCLock lock; 1255 auto* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); 1256 std::ostringstream oss_sig; 1257 oss_sig << *fi_c; 1258 sig = oss_sig.str(); 1259 sig.resize(sig.size() - 2); 1260 } 1261 1262 std::ostringstream os; 1263 std::ostringstream fs; 1264 std::string myMainGroup = group.substr(0, group.find_first_of('.')); 1265 auto it = _maingroup.subgroups.find(myMainGroup); 1266 os << ".. index::\n"; 1267 if (it != _maingroup.subgroups.m.end()) { 1268 os << " pair: " << (*it)->htmlName << "; " << fi->id() << "\n\n"; 1269 } else { 1270 std::cerr << "did not find " << myMainGroup << "\n"; 1271 os << " single: " << fi->id() << "\n\n"; 1272 } 1273 os << ".. code-block:: minizinc\n\n"; 1274 1275 if (fi->ti()->type() == Type::ann()) { 1276 fs << "annotation "; 1277 } else if (fi->ti()->type() == Type::parbool()) { 1278 fs << "test "; 1279 } else if (fi->ti()->type() == Type::varbool()) { 1280 fs << "predicate "; 1281 } else { 1282 fs << "function " << *fi->ti() << ": "; 1283 } 1284 fs << fi->id() << "("; 1285 os << " " << fs.str(); 1286 size_t align = fs.str().size(); 1287 for (unsigned int i = 0; i < fi->params().size(); i++) { 1288 fs << *fi->params()[i]->ti(); 1289 std::ostringstream fid; 1290 fid << *fi->params()[i]->id(); 1291 if (!fid.str().empty()) { 1292 fs << ": " << *fi->params()[i]->id(); 1293 } 1294 if (i < fi->params().size() - 1) { 1295 fs << ", "; 1296 } 1297 } 1298 bool splitArgs = (fs.str().size() > 70); 1299 for (unsigned int i = 0; i < fi->params().size(); i++) { 1300 os << *fi->params()[i]->ti(); 1301 std::ostringstream fid; 1302 fid << *fi->params()[i]->id(); 1303 if (!fid.str().empty()) { 1304 os << ": " << *fi->params()[i]->id(); 1305 } 1306 if (i < fi->params().size() - 1) { 1307 os << ","; 1308 if (splitArgs) { 1309 os << "\n "; 1310 for (auto j = static_cast<unsigned int>(align); (j--) != 0U;) { 1311 os << " "; 1312 } 1313 } else { 1314 os << " "; 1315 } 1316 } 1317 } 1318 os << ")"; 1319 1320 os << "\n\n"; 1321 1322 if (fi->id().c_str()[0] == '\'') { 1323 std::string op = fi->id().substr(1, fi->id().size() - 2); 1324 if (fi->params().size() == 2) { 1325 os << "Usage: ``" << *fi->params()[0]->id() << " " << op << " " << *fi->params()[1]->id() 1326 << "``\n\n"; 1327 } else if (fi->params().size() == 1) { 1328 os << "Usage: ``" << op << " " << *fi->params()[0]->id() << "``\n\n"; 1329 } 1330 } 1331 1332 os << HtmlDocOutput::trim(ds) << "\n\n"; 1333 1334 if (fi->e() != nullptr) { 1335 FunctionI* f_body = fi; 1336 bool alias; 1337 do { 1338 alias = false; 1339 Call* c = Expression::dynamicCast<Call>(f_body->e()); 1340 if ((c != nullptr) && c->argCount() == f_body->params().size()) { 1341 bool sameParams = true; 1342 for (unsigned int i = 0; i < f_body->params().size(); i++) { 1343 Id* ident = c->arg(i)->dynamicCast<Id>(); 1344 if (ident == nullptr || ident->decl() != f_body->params()[i] || 1345 ident->str() != c->decl()->params()[i]->id()->str()) { 1346 sameParams = false; 1347 break; 1348 } 1349 } 1350 if (sameParams) { 1351 alias = true; 1352 f_body = c->decl(); 1353 } 1354 } 1355 } while (alias); 1356 if (f_body->e() != nullptr) { 1357 std::string filename = 1358 std::string(f_body->loc().filename().c_str(), f_body->loc().filename().size()); 1359 size_t filePos = filename.find("std/"); 1360 if (filePos != std::string::npos) { 1361 filePos += 4; 1362 os << ".. only:: builder_html\n\n"; 1363 os << " `More... <https://github.com/MiniZinc/libminizinc/blob/" << MZN_VERSION_MAJOR 1364 << "." << MZN_VERSION_MINOR << "." << MZN_VERSION_PATCH << "/share/minizinc/std/" 1365 << filename.substr(filePos, std::string::npos) << "#L" << f_body->loc().firstLine() 1366 << "-L" << f_body->loc().lastLine() << ">`__\n\n"; 1367 } 1368 } 1369 } 1370 1371 if (!params.empty()) { 1372 os << "Parameters:\n\n"; 1373 for (auto& param : params) { 1374 os << "- ``" << param.first << "``: " << param.second << "\n"; 1375 } 1376 os << "\n"; 1377 } 1378 os << "\n"; 1379 1380 HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, 1381 std::string(fi->id().c_str(), fi->id().size()), sig, os.str()); 1382 HtmlDocOutput::add_to_group(_maingroup, group, di); 1383 } 1384 } 1385}; 1386 1387std::vector<HtmlDocument> RSTPrinter::printRST(EnvI& env, MiniZinc::Model* m, 1388 const std::string& basename, int splitLevel, 1389 bool includeStdLib, bool generateIndex) { 1390 using namespace HtmlDocOutput; 1391 Group g(basename, basename); 1392 FunMap funMap; 1393 CollectFunctionsVisitor fv(env, funMap, includeStdLib); 1394 iter_items(fv, m); 1395 PrintRSTVisitor prv(env, g, funMap, includeStdLib); 1396 iter_items(prv, m); 1397 1398 std::vector<HtmlDocument> ret; 1399 1400 std::ostringstream oss; 1401 oss << Group::rstHeading(g.htmlName, 0); 1402 oss << trim(g.desc) << "\n"; 1403 oss << ".. toctree::\n\n"; 1404 for (auto* sg : g.subgroups.m) { 1405 oss << " " << sg->fullPath << "\n"; 1406 } 1407 1408 ret.emplace_back(g.fullPath, g.htmlName, oss.str()); 1409 1410 for (auto& sg : g.subgroups.m) { 1411 ret.emplace_back(sg->fullPath, sg->htmlName, sg->toRST(0)); 1412 } 1413 return ret; 1414} 1415 1416} // namespace MiniZinc