1 module ddoc; 2 3 class Symbol {} 4 5 string generateDdoc(string macroDefinitionText, bool delegate(Symbol) filter = null) { 6 return expandDdocMacros("$(DDOC)", macroDefinitionText, ["BODY":"cool"]); 7 } 8 9 string expandDdocMacros(string inputText, string macroDefinitionText, string[string] macros) { 10 Macro* tbl; 11 12 parseMacros(&tbl, ddoc_default); 13 14 if(macroDefinitionText !is null) 15 parseMacros(&tbl, macroDefinitionText); 16 17 18 foreach(k, v; macros) 19 Macro.define(&tbl, k, v); 20 21 OutBuffer buf; 22 buf.write(inputText); 23 24 auto end = buf.offset; 25 tbl.expand(&buf, 0, &end, null, 0); 26 27 return finalize(&buf); 28 } 29 30 string finalize(OutBuffer* buf) { 31 ubyte[] d = cast(ubyte[]) (buf.data[0..buf.offset]); 32 string s; 33 s.reserve(d.length); 34 bool skipNext = false; 35 foreach(b; d) { 36 if(skipNext) { 37 skipNext = false; 38 continue; 39 } 40 if(b != 0xff) { 41 s ~= cast(char) b; 42 } else { 43 skipNext = true; 44 } 45 } 46 47 return s; 48 } 49 50 51 52 // Copyright (c) 1999-2006 by Digital Mars 53 // All Rights Reserved 54 // written by Walter Bright 55 // http://www.digitalmars.com 56 // License for redistribution is by either the Artistic License 57 // in artistic.txt, or the GNU General Public License in gnu.txt. 58 // See the included readme.txt for details. 59 60 /* Simple macro text processor. 61 */ 62 63 64 65 66 67 string ddoc_default = " 68 DDOC = <html><head> 69 <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"> 70 <title>$(TITLE)</title> 71 </head><body> 72 <h1>$(TITLE)</h1> 73 $(BODY) 74 <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT)) 75 </body></html> 76 77 B = <b>$0</b> 78 I = <i>$0</i> 79 U = <u>$0</u> 80 P = <p>$0</p> 81 DL = <dl>$0</dl> 82 DT = <dt>$0</dt> 83 DD = <dd>$0</dd> 84 TABLE = <table>$0</table> 85 TR = <tr>$0</tr> 86 TH = <th>$0</th> 87 TD = <td>$0</td> 88 OL = <ol>$0</ol> 89 UL = <ul>$0</ul> 90 LI = <li>$0</li> 91 BIG = <big>$0</big> 92 SMALL = <small>$0</small> 93 BR = <br> 94 LINK = <a href=\"$0\">$0</a> 95 LINK2 = <a href=\"$1\">$+</a> 96 LPAREN= ( 97 RPAREN= ) 98 DOLLAR= $ 99 100 RED = <font color=red>$0</font> 101 BLUE = <font color=blue>$0</font> 102 GREEN = <font color=green>$0</font> 103 YELLOW =<font color=yellow>$0</font> 104 BLACK = <font color=black>$0</font> 105 WHITE = <font color=white>$0</font> 106 107 D_CODE = <pre class=\"d_code\">$0</pre> 108 D_COMMENT = $(GREEN $0) 109 D_STRING = $(RED $0) 110 D_KEYWORD = $(BLUE $0) 111 D_PSYMBOL = $(U $0) 112 D_PARAM = $(I $0) 113 114 DDOC_COMMENT = <!-- $0 --> 115 DDOC_DECL = $(DT $(BIG $0)) 116 DDOC_DECL_DD = $(DD $0) 117 DDOC_DITTO = $(BR)$0 118 DDOC_SECTIONS = $0 119 DDOC_SUMMARY = $0$(BR)$(BR) 120 DDOC_DESCRIPTION = $0$(BR)$(BR) 121 DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR) 122 DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR) 123 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR) 124 DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR) 125 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR) 126 DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR) 127 DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR) 128 DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR) 129 DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR) 130 DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR) 131 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR) 132 DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR) 133 DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR) 134 DDOC_SECTION_H = $(B $0)$(BR) 135 DDOC_SECTION = $0$(BR)$(BR) 136 DDOC_MEMBERS = $(DL $0) 137 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0) 138 DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0) 139 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0) 140 DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0) 141 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0) 142 DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR) 143 DDOC_PARAM_ROW = $(TR $0) 144 DDOC_PARAM_ID = $(TD $0) 145 DDOC_PARAM_DESC = $(TD $0) 146 DDOC_BLANKLINE = $(BR)$(BR) 147 148 DDOC_PSYMBOL = $(U $0) 149 DDOC_KEYWORD = $(B $0) 150 DDOC_PARAM = $(I $0) 151 152 ESCAPES = /</</ 153 />/>/ 154 /&/&/ 155 "; 156 157 struct Escape 158 { 159 const(char)*[256] strings; 160 161 static const(char) *escapeChar(unsigned c) { 162 const(char) *s; 163 164 switch (c) { 165 case '<': 166 s = "<"; 167 break; 168 case '>': 169 s = ">"; 170 break; 171 case '&': 172 s = "&"; 173 break; 174 default: 175 s = null; 176 break; 177 } 178 return s; 179 } 180 } 181 182 183 void parseMacros(Macro** macroTable, in char[] m) { 184 parseMacros(null, macroTable, m.ptr, m.length); 185 } 186 187 /************************************************ 188 * Parse macros out of Macros: section. 189 * Macros are of the form: 190 * name1 = value1 191 * 192 * name2 = value2 193 */ 194 195 void parseMacros(Escape **pescapetable, Macro **pmacrotable, in char *m, unsigned mlen) 196 { 197 const(char) *p = m; 198 unsigned len = mlen; 199 const(char) *pend = p + len; 200 201 const(char) *tempstart; 202 unsigned templen; 203 204 const(char) *namestart; 205 unsigned namelen = 0; // !=0 if line continuation 206 207 const(char) *textstart; 208 unsigned textlen; 209 210 while (p < pend) 211 { 212 // Skip to start of macro 213 while (1) 214 { 215 if (p >= pend) 216 goto Ldone; 217 switch (*p) 218 { 219 case ' ': 220 case '\t': 221 p++; 222 continue; 223 224 case '\n': 225 p++; 226 goto Lcont; 227 228 default: 229 if (isIdStart(p)) 230 break; 231 if (namelen) 232 goto Ltext; // continuation of prev macro 233 goto Lskipline; 234 } 235 break; 236 } 237 tempstart = p; 238 239 while (1) 240 { 241 if (p >= pend) 242 goto Ldone; 243 if (!isIdTail(p)) 244 break; 245 p += utfStride(p); 246 } 247 templen = p - tempstart; 248 249 while (1) 250 { 251 if (p >= pend) 252 goto Ldone; 253 if (!(*p == ' ' || *p == '\t')) 254 break; 255 p++; 256 } 257 258 if (*p != '=') 259 { if (namelen) 260 goto Ltext; // continuation of prev macro 261 goto Lskipline; 262 } 263 p++; 264 if (p >= pend) 265 goto Ldone; 266 267 if (namelen) 268 { // Output existing macro 269 L1: 270 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); 271 //if (icmp("ESCAPES", namestart, namelen) == 0) 272 //parseEscapes(pescapetable, textstart, textlen); 273 //else 274 Macro.define(pmacrotable, namestart[0..namelen].idup, textstart[0..textlen].idup); 275 namelen = 0; 276 if (p >= pend) 277 break; 278 } 279 280 namestart = tempstart; 281 namelen = templen; 282 283 while (p < pend && (*p == ' ' || *p == '\t')) 284 p++; 285 textstart = p; 286 287 Ltext: 288 while (p < pend && *p != '\n') 289 p++; 290 textlen = p - textstart; 291 292 // Remove trailing \r if there is one 293 if (p > m && p[-1] == '\r') 294 textlen--; 295 296 p++; 297 //printf("p = %p, pend = %p\n", p, pend); 298 299 Lcont: 300 continue; 301 302 Lskipline: 303 // Ignore this line 304 while (p < pend && *p++ != '\n') 305 {} 306 } 307 Ldone: 308 if (namelen) 309 goto L1; // write out last one 310 } 311 312 313 314 315 import std.ascii; 316 317 import core.stdc..string; 318 319 char* memdup(char* arg, unsigned arglen) { 320 return arg[0 .. arglen].dup.ptr; 321 } 322 323 struct OutBuffer { 324 void write(string t) { 325 write(t.ptr, t.length); 326 } 327 328 void write(const void *data, unsigned nbytes) { 329 reserve(nbytes); 330 memcpy(this.data + offset, data, nbytes); 331 offset += nbytes; 332 } 333 void reserve(unsigned nbytes) out { assert(data !is null); } body { 334 if (size - offset < nbytes) { 335 size = (offset + nbytes) * 2; 336 auto nl = new char[size]; 337 auto n = nl.ptr; 338 assert(n); 339 if(data) 340 n[0 .. offset] = data[0 .. offset]; 341 data = n; 342 } 343 } 344 345 unsigned insert(unsigned offset, string s) { 346 return insert(offset, s.ptr, s.length); 347 } 348 349 unsigned insert(unsigned offset, const void *p, unsigned nbytes) { 350 spread(offset, nbytes); 351 memmove(data + offset, p, nbytes); 352 return offset + nbytes; 353 } 354 355 void remove(unsigned offset, unsigned nbytes) { 356 memmove(data + offset, data + offset + nbytes, this.offset - (offset + nbytes)); 357 this.offset -= nbytes; 358 } 359 void spread(unsigned offset, unsigned nbytes) { 360 reserve(nbytes); 361 memmove(data + offset + nbytes, data + offset, 362 this.offset - offset); 363 this.offset += nbytes; 364 } 365 366 unsigned offset; 367 char *data; 368 unsigned size; 369 } 370 371 struct Macro { 372 private: 373 Macro *next; // next in list 374 string name; 375 string text; // macro replacement text 376 int inuse; // macro is in use (don't expand) 377 378 this(string name, string text) { 379 next = null; 380 this.name = name; 381 this.text = text; 382 inuse = 0; 383 } 384 385 Macro *search(in char[] name) { 386 Macro *table; 387 388 for (table = &this; table; table = table.next) { 389 if (table.name == name) 390 break; 391 } 392 393 return table; 394 } 395 396 public: 397 static Macro *define(Macro **ptable, string name, string text) { 398 Macro *table; 399 400 //assert(ptable); 401 for (table = *ptable; table; table = table.next) { 402 if(table.name == name) { 403 table.text = text; 404 return table; 405 } 406 } 407 408 table = new Macro(name, text); 409 table.next = *ptable; 410 *ptable = table; 411 return table; 412 } 413 414 /***************************************************** 415 * Expand macro in place in buf. 416 * Only look at the text in buf from start to end. 417 */ 418 419 void expand(OutBuffer *buf, unsigned start, unsigned *pend, 420 char *arg, unsigned arglen) 421 { 422 423 static int nest; 424 if (nest > 100) // limit recursive expansion 425 return; 426 nest++; 427 428 unsigned end = *pend; 429 assert(start <= end); 430 assert(end <= buf.offset); 431 432 /* First pass - replace $0 433 */ 434 arg = memdup(arg, arglen); 435 for (unsigned u = start; u + 1 < end; ) 436 { 437 char *p = buf.data; // buf.data is not loop invariant 438 439 /* Look for $0, but not $$0, and replace it with arg. 440 */ 441 if (p[u] == '$' && (isDigit(p[u + 1]) || p[u + 1] == '+')) 442 { 443 if (u > start && p[u - 1] == '$') 444 { // Don't expand $$0, but replace it with $0 445 buf.remove(u - 1, 1); 446 end--; 447 u += 1; // now u is one past the closing '1' 448 continue; 449 } 450 451 char c = p[u + 1]; 452 int n = (c == '+') ? -1 : c - '0'; 453 454 char *marg; 455 unsigned marglen; 456 extractArgN(arg, arglen, &marg, &marglen, n); 457 if (marglen == 0) 458 { // Just remove macro invocation 459 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); 460 buf.remove(u, 2); 461 end -= 2; 462 } 463 else if (c == '+') 464 { 465 // Replace '$+' with 'arg' 466 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); 467 buf.remove(u, 2); 468 buf.insert(u, marg, marglen); 469 end += marglen - 2; 470 471 // Scan replaced text for further expansion 472 unsigned mend = u + marglen; 473 expand(buf, u, &mend, null, 0); 474 end += mend - (u + marglen); 475 u = mend; 476 } 477 else 478 { 479 // Replace '$1' with '\xFF{arg\xFF}' 480 //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg); 481 buf.data[u] = 0xFF; 482 buf.data[u + 1] = '{'; 483 buf.insert(u + 2, marg, marglen); 484 buf.insert(u + 2 + marglen, "\xFF}".ptr, 2); 485 end += -2 + 2 + marglen + 2; 486 487 // Scan replaced text for further expansion 488 unsigned mend = u + 2 + marglen; 489 expand(buf, u + 2, &mend, null, 0); 490 end += mend - (u + 2 + marglen); 491 u = mend; 492 } 493 //printf("u = %d, end = %d\n", u, end); 494 //printf("#%.*s#\n", end, &buf.data[0]); 495 continue; 496 } 497 498 u++; 499 } 500 501 /* Second pass - replace other macros 502 */ 503 for (unsigned u = start; u + 4 < end; ) 504 { 505 char *p = buf.data; // buf.data is not loop invariant 506 507 /* A valid start of macro expansion is $(c, where c is 508 * an id start character, and not $$(c. 509 */ 510 if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2])) 511 { 512 //printf("\tfound macro start '%c'\n", p[u + 2]); 513 char *name = p + u + 2; 514 unsigned namelen = 0; 515 516 char *marg; 517 unsigned marglen; 518 519 unsigned v; 520 /* Scan forward to find end of macro name and 521 * beginning of macro argument (marg). 522 */ 523 for (v = u + 2; v < end; v++) 524 { char c = p[v]; 525 526 if (!isidchar(c)) 527 { // We've gone past the end of the macro name. 528 namelen = v - (u + 2); 529 break; 530 } 531 } 532 533 v += extractArgN(p + v, end - v, &marg, &marglen, 0); 534 assert(v <= end); 535 536 if (v < end) 537 { // v is on the closing ')' 538 if (u > start && p[u - 1] == '$') 539 { // Don't expand $$(NAME), but replace it with $(NAME) 540 buf.remove(u - 1, 1); 541 end--; 542 u = v; // now u is one past the closing ')' 543 continue; 544 } 545 546 Macro *m = search(name[0 .. namelen]); 547 if (m) 548 { 549 if (m.inuse && marglen == 0) 550 { // Remove macro invocation 551 buf.remove(u, v + 1 - u); 552 end -= v + 1 - u; 553 } 554 else if (m.inuse && arg == marg) 555 { // Recursive expansion; just leave in place 556 557 } 558 else 559 { 560 //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m.namelen, m.name, marglen, marg, m.textlen, m.text); 561 marg = memdup(marg, marglen); 562 // Insert replacement text 563 buf.spread(v + 1, 2 + m.text.length + 2); 564 buf.data[v + 1] = 0xFF; 565 buf.data[v + 2] = '{'; 566 //memcpy(buf.data + v + 3, m.text, m.text.length); 567 buf.data[v+3 .. v+3 + m.text.length] = m.text; 568 buf.data[v + 3 + m.text.length] = 0xFF; 569 buf.data[v + 3 + m.text.length + 1] = '}'; 570 571 end += 2 + m.text.length + 2; 572 573 // Scan replaced text for further expansion 574 m.inuse++; 575 unsigned mend = v + 1 + 2+m.text.length+2; 576 expand(buf, v + 1, &mend, marg, marglen); 577 end += mend - (v + 1 + 2+m.text.length+2); 578 m.inuse--; 579 580 buf.remove(u, v + 1 - u); 581 end -= v + 1 - u; 582 u += mend - (v + 1); 583 //mem.free(marg); 584 //printf("u = %d, end = %d\n", u, end); 585 //printf("#%.*s#\n", end - u, &buf.data[u]); 586 continue; 587 } 588 } 589 else 590 { 591 // Replace $(NAME) with nothing 592 buf.remove(u, v + 1 - u); 593 end -= (v + 1 - u); 594 continue; 595 } 596 } 597 } 598 u++; 599 } 600 //mem.free(arg); 601 *pend = end; 602 nest--; 603 } 604 605 606 607 }; 608 609 610 bool isidstart(char c) { return isAlpha(c) || (c) == '_'; } 611 bool isidchar(char c) { return isAlphaNum(c) || (c) == '_'; } 612 613 /********************************************************** 614 * Given buffer p[0..end], extract argument marg[0..marglen]. 615 * Params: 616 * n 0: get entire argument 617 * 1..9: get nth argument 618 * -1: get 2nd through end 619 */ 620 621 alias uint unsigned; 622 623 unsigned extractArgN(char *p, unsigned end, char **pmarg, unsigned *pmarglen, int n) 624 { 625 /* Scan forward for matching right parenthesis. 626 * Nest parentheses. 627 * Skip over $( and $) 628 * Skip over "..." and '...' strings inside HTML tags. 629 * Skip over <!-- ... -. comments. 630 * Skip over previous macro insertions 631 * Set marglen. 632 */ 633 unsigned parens = 1; 634 char instring = 0; 635 unsigned incomment = 0; 636 unsigned intag = 0; 637 unsigned inexp = 0; 638 unsigned argn = 0; 639 640 unsigned v = 0; 641 642 Largstart: 643 644 // Skip first space, if any, to find the start of the macro argument 645 if (v < end && isWhite(p[v])) 646 v++; 647 *pmarg = p + v; 648 649 for (; v < end; v++) 650 { char c = p[v]; 651 652 switch (c) 653 { 654 case ',': 655 if (!inexp && !instring && !incomment && parens == 1) 656 { 657 argn++; 658 if (argn == 1 && n == -1) 659 { v++; 660 goto Largstart; 661 } 662 if (argn == n) 663 break; 664 if (argn + 1 == n) 665 { v++; 666 goto Largstart; 667 } 668 } 669 continue; 670 671 case '(': 672 if (!inexp && !instring && !incomment) 673 parens++; 674 continue; 675 676 case ')': 677 if (!inexp && !instring && !incomment && --parens == 0) 678 { 679 break; 680 } 681 continue; 682 683 case '"': 684 case '\'': 685 if (!inexp && !incomment && intag) 686 { 687 if (c == instring) 688 instring = 0; 689 else if (!instring) 690 instring = c; 691 } 692 continue; 693 694 case '<': 695 if (!inexp && !instring && !incomment) 696 { 697 if (v + 6 < end && 698 p[v + 1] == '!' && 699 p[v + 2] == '-' && 700 p[v + 3] == '-') 701 { 702 incomment = 1; 703 v += 3; 704 } 705 else if (v + 2 < end && 706 isAlpha(p[v + 1])) 707 intag = 1; 708 } 709 continue; 710 711 case '>': 712 if (!inexp) 713 intag = 0; 714 continue; 715 716 case '-': 717 if (!inexp && 718 !instring && 719 incomment && 720 v + 2 < end && 721 p[v + 1] == '-' && 722 p[v + 2] == '>') 723 { 724 incomment = 0; 725 v += 2; 726 } 727 continue; 728 729 case 0xFF: 730 if (v + 1 < end) 731 { 732 if (p[v + 1] == '{') 733 inexp++; 734 else if (p[v + 1] == '}') 735 inexp--; 736 } 737 continue; 738 739 default: 740 continue; 741 } 742 break; 743 } 744 if (argn == 0 && n == -1) 745 *pmarg = p + v; 746 *pmarglen = p + v - *pmarg; 747 //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg); 748 return v; 749 } 750 751 752 753 754 755 // /////////////////////////////////////////////////////////////////////// 756 // END MACRO.C 757 // /////////////////////////////////////////////////////////////////////// 758 759 760 761 762 763 764 765 766 767 768 769 770 771 /**************************************** 772 * Determine if p points to the start of an identifier. 773 */ 774 775 int isIdStart(in char *p) 776 { 777 unsigned c = *p; 778 if (isAlpha(c) || c == '_') 779 return 1; 780 /*if (c >= 0x80) 781 { size_t i = 0; 782 if (utf_decodeChar(p, 4, &i, &c)) 783 return 0; // ignore errors 784 if (isUniAlpha(c)) 785 return 1; 786 }*/ 787 return 0; 788 } 789 790 /**************************************** 791 * Determine if p points to the rest of an identifier. 792 */ 793 794 int isIdTail(in char *p) 795 { 796 unsigned c = *p; 797 if (isAlphaNum(c) || c == '_') 798 return 1; 799 /* 800 if (c >= 0x80) 801 { size_t i = 0; 802 if (utf_decodeChar(p, 4, &i, &c)) 803 return 0; // ignore errors 804 if (isUniAlpha(c)) 805 return 1; 806 }*/ 807 return 0; 808 } 809 810 /***************************************** 811 * Return number of bytes in UTF character. 812 */ 813 814 int utfStride(in char *p) 815 { 816 unsigned c = *p; 817 if (c < 0x80) 818 return 1; 819 assert(0); 820 /+ 821 size_t i = 0; 822 utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input 823 return i; 824 +/ 825 } 826 827 828 829 /***************************************** 830 * Parse next paragraph out of *pcomment. 831 * Update *pcomment to point past paragraph. 832 * Returns null if no more paragraphs. 833 * If paragraph ends in 'identifier:', 834 * then (*pcomment)[0 .. idlen] is the identifier. 835 */ 836 837 Section*[] parseSections(char *comment) 838 { char *p; 839 char *pstart; 840 char *pend; 841 char *idstart; 842 unsigned idlen; 843 844 Section* summary; 845 Section*[] sections; 846 847 char *name = null; 848 unsigned namelen = 0; 849 850 //printf("parseSections('%s')\n", comment); 851 p = comment; 852 while (*p) 853 { 854 p = skipwhitespace(p); 855 pstart = p; 856 pend = p; 857 858 /* Find end of section, which is ended by one of: 859 * 'identifier:' (but not inside a code section) 860 * '\0' 861 */ 862 idlen = 0; 863 int inCode = 0; 864 while (1) 865 { 866 // Check for start/end of a code section 867 if (*p == '-') 868 { 869 int numdash = 0; 870 while (*p == '-') 871 { 872 ++numdash; 873 p++; 874 } 875 // BUG: handle UTF PS and LS too 876 if (!*p || *p == '\r' || *p == '\n' && numdash >= 3) 877 inCode ^= 1; 878 pend = p; 879 } 880 881 if (!inCode && isIdStart(p)) 882 { 883 char *q = p + utfStride(p); 884 while (isIdTail(q)) 885 q += utfStride(q); 886 if (*q == ':') // identifier: ends it 887 { idlen = q - p; 888 idstart = p; 889 for (pend = p; pend > pstart; pend--) 890 { if (pend[-1] == '\n') 891 break; 892 } 893 p = q + 1; 894 break; 895 } 896 } 897 while (1) 898 { 899 if (!*p) 900 goto L1; 901 if (*p == '\n') 902 { p++; 903 if (*p == '\n' && !summary && !namelen && !inCode) 904 { 905 pend = p; 906 p++; 907 goto L1; 908 } 909 break; 910 } 911 p++; 912 pend = p; 913 } 914 p = skipwhitespace(p); 915 } 916 L1: 917 918 if (namelen || pstart < pend) 919 { 920 Section *s; 921 /* // FIXME 922 if (icmp("Params", name, namelen) == 0) 923 s = new ParamSection(); 924 else if (icmp("Macros", name, namelen) == 0) 925 s = new MacroSection(); 926 else 927 */ 928 s = new Section(); 929 s.name = name; 930 s.namelen = namelen; 931 s.bodyTxt = pstart; 932 s.bodylen = pend - pstart; 933 s.nooutput = 0; 934 935 //printf("Section: '%.*s' = '%.*s'\n", s.namelen, s.name, s.bodylen, s.body); 936 937 sections ~= s; 938 939 //if (!summary && !namelen) 940 //summary = s; 941 } 942 943 if (idlen) 944 { name = idstart; 945 namelen = idlen; 946 } 947 else 948 { name = null; 949 namelen = 0; 950 if (!*p) 951 break; 952 } 953 } 954 955 return sections; 956 } 957 958 959 char *skipwhitespace(char *p) 960 { 961 for (; 1; p++) 962 { switch (*p) 963 { 964 case ' ': 965 case '\t': 966 case '\n': 967 continue; 968 default: 969 } 970 break; 971 } 972 return p; 973 } 974 975 976 977 978 struct Section { 979 char* name; 980 unsigned namelen; 981 char* bodyTxt; 982 unsigned bodylen; 983 int nooutput; 984 } 985 986 int icmp(in char[] what, in char* n, unsigned len) { 987 const(char)[] lol = n[0 .. len]; 988 989 if(what == lol) 990 return 0; 991 992 import std..string; 993 if(toLower(lol) == toLower(what)) 994 return 0; 995 996 return 1; 997 } 998 999 1000 /************************************** 1001 * Parse escapes of the form: 1002 * /c/string/ 1003 * where c is a single character. 1004 * Multiple escapes can be separated 1005 * by whitespace and/or commas. 1006 */ 1007 1008 void parseEscapes(Escape **pescapetable, char *textstart, unsigned textlen) 1009 { Escape *escapetable = *pescapetable; 1010 1011 if (!escapetable) 1012 { escapetable = new Escape; 1013 *pescapetable = escapetable; 1014 } 1015 char *p = textstart; 1016 char *pend = p + textlen; 1017 1018 while (1) 1019 { 1020 while (1) 1021 { 1022 if (p + 4 >= pend) 1023 return; 1024 if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ',')) 1025 break; 1026 p++; 1027 } 1028 if (p[0] != '/' || p[2] != '/') 1029 return; 1030 char c = p[1]; 1031 p += 3; 1032 char *start = p; 1033 while (1) 1034 { 1035 if (p >= pend) 1036 return; 1037 if (*p == '/') 1038 break; 1039 p++; 1040 } 1041 size_t len = p - start; 1042 char[] newbuffer = new char[len + 1]; 1043 char *s = cast(char *)memcpy(newbuffer.ptr, start, len); 1044 s[len] = 0; 1045 escapetable.strings[c] = s; 1046 //printf("%c = '%s'\n", c, s); 1047 p++; 1048 } 1049 } 1050 1051 1052 /**************************************************** 1053 */ 1054 1055 int isKeyword(char *p, unsigned len) 1056 { 1057 static string[] table = [ "true", "false", "null" ]; 1058 1059 foreach(i; table) 1060 if(i == p[0..len]) 1061 return 1; 1062 return 0; 1063 } 1064 1065 1066 /**************************************************** 1067 * Having unmatched parentheses can hose the output of Ddoc, 1068 * as the macros depend on properly nested parentheses. 1069 1070 * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN). 1071 */ 1072 void escapeStrayParenthesis(OutBuffer *buf, unsigned start) 1073 { 1074 unsigned par_open = 0; 1075 1076 for (unsigned u = start; u < buf.offset; u++) 1077 { 1078 char c = buf.data[u]; 1079 switch(c) 1080 { 1081 case '(': 1082 par_open++; 1083 break; 1084 1085 case ')': 1086 if (par_open == 0) 1087 { 1088 //stray ')' 1089 //if (global.params.warnings) 1090 //warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output." 1091 //" Use $(RPAREN) instead for unpaired right parentheses."); 1092 buf.remove(u, 1); //remove the ) 1093 buf.insert(u, "$(RPAREN)"); //insert this instead 1094 u += 8; //skip over newly inserted macro 1095 } 1096 else 1097 par_open--; 1098 break; 1099 1100 default: 1101 } 1102 } 1103 1104 if (par_open) // if any unmatched lparens 1105 { par_open = 0; 1106 for (unsigned u = buf.offset; u > start;) 1107 { u--; 1108 char c = buf.data[u]; 1109 switch(c) 1110 { 1111 case ')': 1112 par_open++; 1113 break; 1114 1115 case '(': 1116 if (par_open == 0) 1117 { 1118 //stray '(' 1119 //if (global.params.warnings) 1120 //warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output." 1121 //" Use $(LPAREN) instead for unpaired left parentheses."); 1122 buf.remove(u, 1); //remove the ( 1123 buf.insert(u, "$(LPAREN)"); //insert this instead 1124 } 1125 else 1126 par_open--; 1127 break; 1128 default: 1129 } 1130 } 1131 } 1132 } 1133 1134 /**************************************************** 1135 * Having unmatched parentheses can hose the output of Ddoc, 1136 * as the macros depend on properly nested parentheses. 1137 * This function replaces all ( with $(LPAREN) and ) with $(RPAREN) 1138 * to preserve text literally. This also means macros in the 1139 * text won't be expanded. 1140 */ 1141 void escapeDdocString(OutBuffer *buf, unsigned start) 1142 { 1143 for (unsigned u = start; u < buf.offset; u++) 1144 { 1145 char c = buf.data[u]; 1146 switch(c) 1147 { 1148 case '$': 1149 buf.remove(u, 1); 1150 buf.insert(u, "$(DOLLAR)"); 1151 u += 8; 1152 break; 1153 1154 case '(': 1155 buf.remove(u, 1); //remove the ( 1156 buf.insert(u, "$(LPAREN)"); //insert this instead 1157 u += 8; //skip over newly inserted macro 1158 break; 1159 1160 case ')': 1161 buf.remove(u, 1); //remove the ) 1162 buf.insert(u, "$(RPAREN)"); //insert this instead 1163 u += 8; //skip over newly inserted macro 1164 break; 1165 default: 1166 } 1167 } 1168 } 1169 1170 1171 1172 1173 1174 1175 1176 /+ 1177 1178 1179 1180 /**************************************************** 1181 */ 1182 1183 void Module::gendocfile() 1184 { 1185 static OutBuffer mbuf; 1186 static int mbuf_done; 1187 1188 OutBuffer buf; 1189 1190 //printf("Module::gendocfile()\n"); 1191 1192 if (!mbuf_done) // if not already read the ddoc files 1193 { mbuf_done = 1; 1194 1195 // Use our internal default 1196 mbuf.write(ddoc_default, sizeof(ddoc_default) - 1); 1197 1198 // Override with DDOCFILE specified in the sc.ini file 1199 char *p = getenv("DDOCFILE"); 1200 if (p) 1201 global.params.ddocfiles->shift(p); 1202 1203 // Override with the ddoc macro files from the command line 1204 for (size_t i = 0; i < global.params.ddocfiles->dim; i++) 1205 { 1206 FileName f((*global.params.ddocfiles)[i], 0); 1207 File file(&f); 1208 file.readv(); 1209 // BUG: convert file contents to UTF-8 before use 1210 1211 //printf("file: '%.*s'\n", file.len, file.buffer); 1212 mbuf.write(file.buffer, file.len); 1213 } 1214 } 1215 DocComment::parseMacros(&escapetable, ¯otable, mbuf.data, mbuf.offset); 1216 1217 Scope *sc = Scope::createGlobal(this); // create root scope 1218 sc->docbuf = &buf; 1219 1220 DocComment *dc = DocComment::parse(sc, this, comment); 1221 dc->pmacrotable = ¯otable; 1222 dc->pescapetable = &escapetable; 1223 1224 // Generate predefined macros 1225 1226 // Set the title to be the name of the module 1227 { const char *p = toPrettyChars(); 1228 Macro::define(¯otable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p)); 1229 } 1230 1231 // Set time macros 1232 { time_t t; 1233 time(&t); 1234 char *p = ctime(&t); 1235 p = mem.strdup(p); 1236 Macro::define(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p)); 1237 Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4); 1238 } 1239 1240 char *srcfilename = srcfile->toChars(); 1241 Macro::define(¯otable, (unsigned char *)"SRCFILENAME", 11, (unsigned char *)srcfilename, strlen(srcfilename)); 1242 1243 char *docfilename = docfile->toChars(); 1244 Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename)); 1245 1246 if (dc->copyright) 1247 { 1248 dc->copyright->nooutput = 1; 1249 Macro::define(¯otable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); 1250 } 1251 1252 buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars()); 1253 if (isDocFile) 1254 { 1255 size_t commentlen = strlen((char *)comment); 1256 if (dc->macros) 1257 { 1258 commentlen = dc->macros->name - comment; 1259 dc->macros->write(dc, sc, this, sc->docbuf); 1260 } 1261 sc->docbuf->write(comment, commentlen); 1262 highlightText(NULL, this, sc->docbuf, 0); 1263 } 1264 else 1265 { 1266 dc->writeSections(sc, this, sc->docbuf); 1267 emitMemberComments(sc); 1268 } 1269 1270 //printf("BODY= '%.*s'\n", buf.offset, buf.data); 1271 Macro::define(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset); 1272 1273 OutBuffer buf2; 1274 buf2.writestring("$(DDOC)\n"); 1275 unsigned end = buf2.offset; 1276 macrotable->expand(&buf2, 0, &end, NULL, 0); 1277 1278 #if 1 1279 /* Remove all the escape sequences from buf2, 1280 * and make CR-LF the newline. 1281 */ 1282 { 1283 buf.setsize(0); 1284 buf.reserve(buf2.offset); 1285 unsigned char *p = buf2.data; 1286 for (unsigned j = 0; j < buf2.offset; j++) 1287 { 1288 unsigned char c = p[j]; 1289 if (c == 0xFF && j + 1 < buf2.offset) 1290 { 1291 j++; 1292 continue; 1293 } 1294 if (c == '\n') 1295 buf.writeByte('\r'); 1296 else if (c == '\r') 1297 { 1298 buf.writestring("\r\n"); 1299 if (j + 1 < buf2.offset && p[j + 1] == '\n') 1300 { 1301 j++; 1302 } 1303 continue; 1304 } 1305 buf.writeByte(c); 1306 } 1307 } 1308 1309 // Transfer image to file 1310 assert(docfile); 1311 docfile->setbuffer(buf.data, buf.offset); 1312 docfile->ref = 1; 1313 char *pt = FileName::path(docfile->toChars()); 1314 if (*pt) 1315 FileName::ensurePathExists(pt); 1316 mem.free(pt); 1317 docfile->writev(); 1318 #else 1319 /* Remove all the escape sequences from buf2 1320 */ 1321 { unsigned i = 0; 1322 unsigned char *p = buf2.data; 1323 for (unsigned j = 0; j < buf2.offset; j++) 1324 { 1325 if (p[j] == 0xFF && j + 1 < buf2.offset) 1326 { 1327 j++; 1328 continue; 1329 } 1330 p[i] = p[j]; 1331 i++; 1332 } 1333 buf2.setsize(i); 1334 } 1335 1336 // Transfer image to file 1337 docfile->setbuffer(buf2.data, buf2.offset); 1338 docfile->ref = 1; 1339 char *pt = FileName::path(docfile->toChars()); 1340 if (*pt) 1341 FileName::ensurePathExists(pt); 1342 mem.free(pt); 1343 docfile->writev(); 1344 #endif 1345 } 1346 1347 +/