1:
2: // Compiler implementation of the D programming language
3: // Copyright (c) 1999-2011 by Digital Mars
4: // All Rights Reserved
5: // written by Walter Bright
6: // http://www.digitalmars.com
7: // License for redistribution is by either the Artistic License
8: // in artistic.txt, or the GNU General Public License in gnu.txt.
9: // See the included readme.txt for details.
10:
11: // This implements the Ddoc capability.
12:
13: #include <stdio.h>
14: #include <string.h>
15: #include <time.h>
16: #include <ctype.h>
17: static char __file__[] = __FILE__; /* for tassert.h */
18: #include "tassert.h"
19:
20: #include "rmem.h"
21: #include "root.h"
22:
23: #include "mars.h"
24: #include "dsymbol.h"
25: #include "macro.h"
26: #include "template.h"
27: #include "lexer.h"
28: #include "aggregate.h"
29: #include "declaration.h"
30: #include "enum.h"
31: #include "id.h"
32: #include "module.h"
33: #include "scope.h"
34: #include "hdrgen.h"
35: #include "doc.h"
36: #include "mtype.h"
37: #include "utf.h"
38:
39: struct Escape
40: {
41: const char *strings[256];
42:
43: static const char *escapeChar(unsigned c);
44: };
45:
46: struct Section
47: {
48: unsigned char *name;
49: unsigned namelen;
50:
51: unsigned char *body;
52: unsigned bodylen;
53:
54: int nooutput;
55:
56: virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
57: };
58:
59: struct ParamSection : Section
60: {
61: void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
62: };
63:
64: struct MacroSection : Section
65: {
66: void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
67: };
68:
69: typedef ArrayBase<Section> Sections;
70:
71: struct DocComment
72: {
73: Sections sections; // Section*[]
74:
75: Section *summary;
76: Section *copyright;
77: Section *macros;
78: Macro **pmacrotable;
79: Escape **pescapetable;
80:
81: DocComment();
82:
83: static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment);
84: static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen);
85: static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen);
86:
87: void parseSections(unsigned char *comment);
88: void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf);
89: };
90:
91:
92: int cmp(const char *stringz, void *s, size_t slen);
93: int icmp(const char *stringz, void *s, size_t slen);
94: int isDitto(unsigned char *comment);
95: unsigned char *skipwhitespace(unsigned char *p);
96: unsigned skiptoident(OutBuffer *buf, size_t i);
97: unsigned skippastident(OutBuffer *buf, size_t i);
98: unsigned skippastURL(OutBuffer *buf, size_t i);
99: void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
100: void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
101: void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
102: Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len);
103:
104: int isIdStart(unsigned char *p);
105: int isIdTail(unsigned char *p);
106: int utfStride(unsigned char *p);
107:
108: static unsigned char ddoc_default[] = "\
109: DDOC = <html><head>\n\
110: <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
111: <title>$(TITLE)</title>\n\
112: </head><body>\n\
113: <h1>$(TITLE)</h1>\n\
114: $(BODY)\n\
115: <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\
116: </body></html>\n\
117: \n\
118: B = <b>$0</b>\n\
119: I = <i>$0</i>\n\
120: U = <u>$0</u>\n\
121: P = <p>$0</p>\n\
122: DL = <dl>$0</dl>\n\
123: DT = <dt>$0</dt>\n\
124: DD = <dd>$0</dd>\n\
125: TABLE = <table>$0</table>\n\
126: TR = <tr>$0</tr>\n\
127: TH = <th>$0</th>\n\
128: TD = <td>$0</td>\n\
129: OL = <ol>$0</ol>\n\
130: UL = <ul>$0</ul>\n\
131: LI = <li>$0</li>\n\
132: BIG = <big>$0</big>\n\
133: SMALL = <small>$0</small>\n\
134: BR = <br>\n\
135: LINK = <a href=\"$0\">$0</a>\n\
136: LINK2 = <a href=\"$1\">$+</a>\n\
137: LPAREN= (\n\
138: RPAREN= )\n\
139: DOLLAR= $\n\
140: \n\
141: RED = <font color=red>$0</font>\n\
142: BLUE = <font color=blue>$0</font>\n\
143: GREEN = <font color=green>$0</font>\n\
144: YELLOW =<font color=yellow>$0</font>\n\
145: BLACK = <font color=black>$0</font>\n\
146: WHITE = <font color=white>$0</font>\n\
147: \n\
148: D_CODE = <pre class=\"d_code\">$0</pre>\n\
149: D_COMMENT = $(GREEN $0)\n\
150: D_STRING = $(RED $0)\n\
151: D_KEYWORD = $(BLUE $0)\n\
152: D_PSYMBOL = $(U $0)\n\
153: D_PARAM = $(I $0)\n\
154: \n\
155: DDOC_COMMENT = <!-- $0 -->\n\
156: DDOC_DECL = $(DT $(BIG $0))\n\
157: DDOC_DECL_DD = $(DD $0)\n\
158: DDOC_DITTO = $(BR)$0\n\
159: DDOC_SECTIONS = $0\n\
160: DDOC_SUMMARY = $0$(BR)$(BR)\n\
161: DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
162: DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
163: DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
164: DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
165: DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
166: DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
167: DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
168: DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
169: DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
170: DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
171: DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
172: DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
173: DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
174: DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
175: DDOC_SECTION_H = $(B $0)$(BR)\n\
176: DDOC_SECTION = $0$(BR)$(BR)\n\
177: DDOC_MEMBERS = $(DL $0)\n\
178: DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
179: DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
180: DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
181: DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
182: DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
183: DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
184: DDOC_PARAM_ROW = $(TR $0)\n\
185: DDOC_PARAM_ID = $(TD $0)\n\
186: DDOC_PARAM_DESC = $(TD $0)\n\
187: DDOC_BLANKLINE = $(BR)$(BR)\n\
188: \n\
189: DDOC_PSYMBOL = $(U $0)\n\
190: DDOC_KEYWORD = $(B $0)\n\
191: DDOC_PARAM = $(I $0)\n\
192: \n\
193: ESCAPES = /</</\n\
194: />/>/\n\
195: /&/&/\n\
196: ";
197:
198: static char ddoc_decl_s[] = "$(DDOC_DECL ";
199: static char ddoc_decl_e[] = ")\n";
200:
201: static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
202: static char ddoc_decl_dd_e[] = ")\n";
203:
204:
205: /****************************************************
206: */
207:
208: void Module::gendocfile()
209: {
210: static OutBuffer mbuf;
211: static int mbuf_done;
212:
213: OutBuffer buf;
214:
215: //printf("Module::gendocfile()\n");
216:
217: if (!mbuf_done) // if not already read the ddoc files
218: { mbuf_done = 1;
219:
220: // Use our internal default
221: mbuf.write(ddoc_default, sizeof(ddoc_default) - 1);
222:
223: // Override with DDOCFILE specified in the sc.ini file
224: char *p = getenv("DDOCFILE");
warning C4996: 'getenv': This function or variable may be unsafe. Consider using _dupenv_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\stdlib.h(433) : see declaration of 'getenv'
225: if (p)
226: global.params.ddocfiles->shift(p);
227:
228: // Override with the ddoc macro files from the command line
229: for (int i = 0; i < global.params.ddocfiles->dim; i++)
warning C4018: '<' : signed/unsigned mismatch
230: {
231: FileName f(global.params.ddocfiles->tdata()[i], 0);
232: File file(&f);
233: file.readv();
234: // BUG: convert file contents to UTF-8 before use
235:
236: //printf("file: '%.*s'\n", file.len, file.buffer);
237: mbuf.write(file.buffer, file.len);
238: }
239: }
240: DocComment::parseMacros(&escapetable, ¯otable, mbuf.data, mbuf.offset);
241:
242: Scope *sc = Scope::createGlobal(this); // create root scope
243: sc->docbuf = &buf;
244:
245: DocComment *dc = DocComment::parse(sc, this, comment);
246: dc->pmacrotable = ¯otable;
247: dc->pescapetable = &escapetable;
248:
249: // Generate predefined macros
250:
251: // Set the title to be the name of the module
252: { const char *p = toPrettyChars();
253: Macro::define(¯otable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p));
254: }
255:
256: time_t t;
257: time(&t);
258: char *p = ctime(&t);
warning C4996: 'ctime': This function or variable may be unsafe. Consider using ctime_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\time.inl(86) : see declaration of 'ctime'
259: p = mem.strdup(p);
260: Macro::define(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p));
261: Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4);
262:
263: char *docfilename = docfile->toChars();
264: Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename));
265:
266: if (dc->copyright)
267: {
268: dc->copyright->nooutput = 1;
269: Macro::define(¯otable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
270: }
271:
272: buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
273: if (isDocFile)
274: {
275: size_t commentlen = strlen((char *)comment);
276: if (dc->macros)
277: {
278: commentlen = dc->macros->name - comment;
279: dc->macros->write(dc, sc, this, sc->docbuf);
280: }
281: sc->docbuf->write(comment, commentlen);
282: highlightText(NULL, this, sc->docbuf, 0);
283: }
284: else
285: {
286: dc->writeSections(sc, this, sc->docbuf);
287: emitMemberComments(sc);
288: }
289:
290: //printf("BODY= '%.*s'\n", buf.offset, buf.data);
291: Macro::define(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset);
292:
293: OutBuffer buf2;
294: buf2.writestring("$(DDOC)\n");
295: unsigned end = buf2.offset;
296: macrotable->expand(&buf2, 0, &end, NULL, 0);
297:
298: #if 1
299: /* Remove all the escape sequences from buf2,
300: * and make CR-LF the newline.
301: */
302: {
303: buf.setsize(0);
304: buf.reserve(buf2.offset);
305: unsigned char *p = buf2.data;
warning C6246: Local declaration of 'p' hides declaration of the same name in outer scope. For additional information, see previous declaration at line '258' of 'c:\projects\extern\d\dmd\src\doc.c': Lines: 258
306: for (unsigned j = 0; j < buf2.offset; j++)
307: {
308: unsigned char c = p[j];
309: if (c == 0xFF && j + 1 < buf2.offset)
310: {
311: j++;
312: continue;
313: }
314: if (c == '\n')
315: buf.writeByte('\r');
316: else if (c == '\r')
317: {
318: buf.writestring("\r\n");
319: if (j + 1 < buf2.offset && p[j + 1] == '\n')
320: {
321: j++;
322: }
323: continue;
324: }
325: buf.writeByte(c);
326: }
327: }
328:
329: // Transfer image to file
330: assert(docfile);
331: docfile->setbuffer(buf.data, buf.offset);
332: docfile->ref = 1;
333: char *pt = FileName::path(docfile->toChars());
334: if (*pt)
335: FileName::ensurePathExists(pt);
336: mem.free(pt);
337: docfile->writev();
338: #else
339: /* Remove all the escape sequences from buf2
340: */
341: { unsigned i = 0;
342: unsigned char *p = buf2.data;
343: for (unsigned j = 0; j < buf2.offset; j++)
344: {
345: if (p[j] == 0xFF && j + 1 < buf2.offset)
346: {
347: j++;
348: continue;
349: }
350: p[i] = p[j];
351: i++;
352: }
353: buf2.setsize(i);
354: }
355:
356: // Transfer image to file
357: docfile->setbuffer(buf2.data, buf2.offset);
358: docfile->ref = 1;
359: char *pt = FileName::path(docfile->toChars());
360: if (*pt)
361: FileName::ensurePathExists(pt);
362: mem.free(pt);
363: docfile->writev();
364: #endif
365: }
366:
367: /****************************************************
368: * Having unmatched parentheses can hose the output of Ddoc,
369: * as the macros depend on properly nested parentheses.
370: * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
371: * to preserve text literally. This also means macros in the
372: * text won't be expanded.
373: */
374: void escapeDdocString(OutBuffer *buf, unsigned start)
375: {
376: for (unsigned u = start; u < buf->offset; u++)
377: {
378: unsigned char c = buf->data[u];
379: switch(c)
380: {
381: case '$':
382: buf->remove(u, 1);
383: buf->insert(u, "$(DOLLAR)", 9);
384: u += 8;
385: break;
386:
387: case '(':
388: buf->remove(u, 1); //remove the (
389: buf->insert(u, "$(LPAREN)", 9); //insert this instead
390: u += 8; //skip over newly inserted macro
391: break;
392:
393: case ')':
394: buf->remove(u, 1); //remove the )
395: buf->insert(u, "$(RPAREN)", 9); //insert this instead
396: u += 8; //skip over newly inserted macro
397: break;
398: }
399: }
400: }
401:
402: /****************************************************
403: * Having unmatched parentheses can hose the output of Ddoc,
404: * as the macros depend on properly nested parentheses.
405:
406: * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
407: */
408: void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc)
409: {
410: unsigned par_open = 0;
411:
412: for (unsigned u = start; u < buf->offset; u++)
413: {
414: unsigned char c = buf->data[u];
415: switch(c)
416: {
417: case '(':
418: par_open++;
419: break;
420:
421: case ')':
422: if (par_open == 0)
423: {
424: //stray ')'
425: if (global.params.warnings)
426: warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
427: " Use $(RPAREN) instead for unpaired right parentheses.");
428: buf->remove(u, 1); //remove the )
429: buf->insert(u, "$(RPAREN)", 9); //insert this instead
430: u += 8; //skip over newly inserted macro
431: }
432: else
433: par_open--;
434: break;
435: #if 0
436: // For this to work, loc must be set to the beginning of the passed
437: // text which is currently not possible
438: // (loc is set to the Loc of the Dsymbol)
439: case '\n':
440: loc.linnum++;
441: break;
442: #endif
443: }
444: }
445:
446: if (par_open) // if any unmatched lparens
447: { par_open = 0;
448: for (unsigned u = buf->offset; u > start;)
449: { u--;
450: unsigned char c = buf->data[u];
451: switch(c)
452: {
453: case ')':
454: par_open++;
455: break;
456:
457: case '(':
458: if (par_open == 0)
459: {
460: //stray '('
461: if (global.params.warnings)
462: warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
463: " Use $(LPAREN) instead for unpaired left parentheses.");
464: buf->remove(u, 1); //remove the (
465: buf->insert(u, "$(LPAREN)", 9); //insert this instead
466: }
467: else
468: par_open--;
469: break;
470: }
471: }
472: }
473: }
474:
475: /******************************* emitComment **********************************/
476:
477: /*
478: * Emit doc comment to documentation file
479: */
480:
481: void Dsymbol::emitDitto(Scope *sc)
482: {
483: //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars());
484: OutBuffer *buf = sc->docbuf;
485: unsigned o;
486: OutBuffer b;
487:
488: b.writestring("$(DDOC_DITTO ");
489: o = b.offset;
490: toDocBuffer(&b);
491: //printf("b: '%.*s'\n", b.offset, b.data);
492: /* If 'this' is a function template, then highlightCode() was
493: * already run by FuncDeclaration::toDocbuffer().
494: */
495: TemplateDeclaration *td;
496: if (parent &&
497: (td = parent->isTemplateDeclaration()) != NULL &&
498: td->onemember == this)
499: {
500: }
501: else
502: highlightCode(sc, this, &b, o);
503: b.writeByte(')');
504: buf->spread(sc->lastoffset, b.offset);
505: memcpy(buf->data + sc->lastoffset, b.data, b.offset);
506: sc->lastoffset += b.offset;
507: }
508:
509: void ScopeDsymbol::emitMemberComments(Scope *sc)
510: {
511: //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
512: OutBuffer *buf = sc->docbuf;
513:
514: if (members)
515: { const char *m = "$(DDOC_MEMBERS \n";
516:
517: if (isModule())
518: m = "$(DDOC_MODULE_MEMBERS \n";
519: else if (isClassDeclaration())
520: m = "$(DDOC_CLASS_MEMBERS \n";
521: else if (isStructDeclaration())
522: m = "$(DDOC_STRUCT_MEMBERS \n";
523: else if (isEnumDeclaration())
524: m = "$(DDOC_ENUM_MEMBERS \n";
525: else if (isTemplateDeclaration())
526: m = "$(DDOC_TEMPLATE_MEMBERS \n";
527:
528: unsigned offset1 = buf->offset; // save starting offset
529: buf->writestring(m);
530: unsigned offset2 = buf->offset; // to see if we write anything
531: sc = sc->push(this);
532: for (int i = 0; i < members->dim; i++)
warning C4018: '<' : signed/unsigned mismatch
533: {
534: Dsymbol *s = members->tdata()[i];
535: //printf("\ts = '%s'\n", s->toChars());
536: s->emitComment(sc);
537: }
538: sc->pop();
539: if (buf->offset == offset2)
540: {
541: /* Didn't write out any members, so back out last write
542: */
543: buf->offset = offset1;
544: }
545: else
546: buf->writestring(")\n");
547: }
548: }
549:
550: void emitProtection(OutBuffer *buf, PROT prot)
551: {
552: const char *p;
553:
554: switch (prot)
555: {
556: case PROTpackage: p = "package"; break;
557: case PROTprotected: p = "protected"; break;
558: case PROTexport: p = "export"; break;
559: default: p = NULL; break;
560: }
561: if (p)
562: buf->printf("%s ", p);
563: }
564:
565: void Dsymbol::emitComment(Scope *sc) { }
566: void InvariantDeclaration::emitComment(Scope *sc) { }
567: #if DMDV2
568: void PostBlitDeclaration::emitComment(Scope *sc) { }
569: #endif
570: void DtorDeclaration::emitComment(Scope *sc) { }
571: void StaticCtorDeclaration::emitComment(Scope *sc) { }
572: void StaticDtorDeclaration::emitComment(Scope *sc) { }
573: void ClassInfoDeclaration::emitComment(Scope *sc) { }
574: void ModuleInfoDeclaration::emitComment(Scope *sc) { }
575: void TypeInfoDeclaration::emitComment(Scope *sc) { }
576:
577:
578: void Declaration::emitComment(Scope *sc)
579: {
580: //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
581: //printf("type = %p\n", type);
582:
583: if (protection == PROTprivate || !ident ||
584: (!type && !isCtorDeclaration() && !isAliasDeclaration()))
585: return;
586: if (!comment)
587: return;
588:
589: OutBuffer *buf = sc->docbuf;
590: DocComment *dc = DocComment::parse(sc, this, comment);
591: unsigned o;
592:
593: if (!dc)
594: {
595: emitDitto(sc);
596: return;
597: }
598: dc->pmacrotable = &sc->module->macrotable;
599:
600: buf->writestring(ddoc_decl_s);
601: o = buf->offset;
602: toDocBuffer(buf);
603: highlightCode(sc, this, buf, o);
604: sc->lastoffset = buf->offset;
605: buf->writestring(ddoc_decl_e);
606:
607: buf->writestring(ddoc_decl_dd_s);
608: dc->writeSections(sc, this, buf);
609: buf->writestring(ddoc_decl_dd_e);
610: }
611:
612: void AggregateDeclaration::emitComment(Scope *sc)
613: {
614: //printf("AggregateDeclaration::emitComment() '%s'\n", toChars());
615: if (prot() == PROTprivate)
616: return;
617: if (!comment)
618: return;
619:
620: OutBuffer *buf = sc->docbuf;
621: DocComment *dc = DocComment::parse(sc, this, comment);
622:
623: if (!dc)
624: {
625: emitDitto(sc);
626: return;
627: }
628: dc->pmacrotable = &sc->module->macrotable;
629:
630: buf->writestring(ddoc_decl_s);
631: toDocBuffer(buf);
632: sc->lastoffset = buf->offset;
633: buf->writestring(ddoc_decl_e);
634:
635: buf->writestring(ddoc_decl_dd_s);
636: dc->writeSections(sc, this, buf);
637: emitMemberComments(sc);
638: buf->writestring(ddoc_decl_dd_e);
639: }
640:
641: void TemplateDeclaration::emitComment(Scope *sc)
642: {
643: //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind());
644: if (prot() == PROTprivate)
645: return;
646:
647: unsigned char *com = comment;
648: int hasmembers = 1;
649:
650: Dsymbol *ss = this;
651:
652: if (onemember)
653: {
654: ss = onemember->isAggregateDeclaration();
655: if (!ss)
656: {
657: ss = onemember->isFuncDeclaration();
658: if (ss)
659: { hasmembers = 0;
660: if (com != ss->comment)
661: com = Lexer::combineComments(com, ss->comment);
662: }
663: else
664: ss = this;
665: }
666: }
667:
668: if (!com)
669: return;
670:
671: OutBuffer *buf = sc->docbuf;
672: DocComment *dc = DocComment::parse(sc, this, com);
673: unsigned o;
674:
675: if (!dc)
676: {
677: ss->emitDitto(sc);
678: return;
679: }
680: dc->pmacrotable = &sc->module->macrotable;
681:
682: buf->writestring(ddoc_decl_s);
683: o = buf->offset;
684: ss->toDocBuffer(buf);
685: if (ss == this)
686: highlightCode(sc, this, buf, o);
687: sc->lastoffset = buf->offset;
688: buf->writestring(ddoc_decl_e);
689:
690: buf->writestring(ddoc_decl_dd_s);
691: dc->writeSections(sc, this, buf);
692: if (hasmembers)
693: ((ScopeDsymbol *)ss)->emitMemberComments(sc);
694: buf->writestring(ddoc_decl_dd_e);
695: }
696:
697: void EnumDeclaration::emitComment(Scope *sc)
698: {
699: if (prot() == PROTprivate)
700: return;
701: // if (!comment)
702: { if (isAnonymous() && members)
703: {
704: for (int i = 0; i < members->dim; i++)
warning C4018: '<' : signed/unsigned mismatch
705: {
706: Dsymbol *s = members->tdata()[i];
707: s->emitComment(sc);
708: }
709: return;
710: }
711: }
712: if (!comment)
713: return;
714: if (isAnonymous())
715: return;
716:
717: OutBuffer *buf = sc->docbuf;
718: DocComment *dc = DocComment::parse(sc, this, comment);
719:
720: if (!dc)
721: {
722: emitDitto(sc);
723: return;
724: }
725: dc->pmacrotable = &sc->module->macrotable;
726:
727: buf->writestring(ddoc_decl_s);
728: toDocBuffer(buf);
729: sc->lastoffset = buf->offset;
730: buf->writestring(ddoc_decl_e);
731:
732: buf->writestring(ddoc_decl_dd_s);
733: dc->writeSections(sc, this, buf);
734: emitMemberComments(sc);
735: buf->writestring(ddoc_decl_dd_e);
736: }
737:
738: void EnumMember::emitComment(Scope *sc)
739: {
740: //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
741: if (prot() == PROTprivate)
742: return;
743: if (!comment)
744: return;
745:
746: OutBuffer *buf = sc->docbuf;
747: DocComment *dc = DocComment::parse(sc, this, comment);
748: unsigned o;
749:
750: if (!dc)
751: {
752: emitDitto(sc);
753: return;
754: }
755: dc->pmacrotable = &sc->module->macrotable;
756:
757: buf->writestring(ddoc_decl_s);
758: o = buf->offset;
759: toDocBuffer(buf);
760: highlightCode(sc, this, buf, o);
761: sc->lastoffset = buf->offset;
762: buf->writestring(ddoc_decl_e);
763:
764: buf->writestring(ddoc_decl_dd_s);
765: dc->writeSections(sc, this, buf);
766: buf->writestring(ddoc_decl_dd_e);
767: }
768:
769: /******************************* toDocBuffer **********************************/
770:
771: void Dsymbol::toDocBuffer(OutBuffer *buf)
772: {
773: //printf("Dsymbol::toDocbuffer() %s\n", toChars());
774: HdrGenState hgs;
775:
776: hgs.ddoc = 1;
777: toCBuffer(buf, &hgs);
778: }
779:
780: void prefix(OutBuffer *buf, Dsymbol *s)
781: {
782: if (s->isDeprecated())
783: buf->writestring("deprecated ");
784: Declaration *d = s->isDeclaration();
785: if (d)
786: {
787: emitProtection(buf, d->protection);
788: if (d->isAbstract())
789: buf->writestring("abstract ");
790: if (d->isStatic())
791: buf->writestring("static ");
792: if (d->isConst())
793: buf->writestring("const ");
794: #if DMDV2
795: if (d->isImmutable())
796: buf->writestring("immutable ");
797: #endif
798: if (d->isFinal())
799: buf->writestring("final ");
800: if (d->isSynchronized())
801: buf->writestring("synchronized ");
802: }
803: }
804:
805: void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclaration *td)
806: {
807: //printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--");
808: if (decl->ident)
809: {
810: prefix(buf, decl);
811:
812: if (decl->type)
813: { HdrGenState hgs;
814: hgs.ddoc = 1;
815: Type *origType = decl->originalType ? decl->originalType : decl->type;
816: if (origType->ty == Tfunction)
817: {
818: TypeFunction *attrType = (TypeFunction*)(decl->ident == Id::ctor ? origType : decl->type);
819: ((TypeFunction*)origType)->toCBufferWithAttributes(buf, decl->ident, &hgs, attrType, td);
820: }
821: else
822: origType->toCBuffer(buf, decl->ident, &hgs);
823: }
824: else
825: buf->writestring(decl->ident->toChars());
826: buf->writestring(";\n");
827: }
828: }
829:
830: void Declaration::toDocBuffer(OutBuffer *buf)
831: {
832: declarationToDocBuffer(this, buf, NULL);
833: }
834:
835: void AliasDeclaration::toDocBuffer(OutBuffer *buf)
836: {
837: //printf("AliasDeclaration::toDocbuffer() %s\n", toChars());
838: if (ident)
839: {
840: if (isDeprecated())
841: buf->writestring("deprecated ");
842:
843: emitProtection(buf, protection);
844: buf->writestring("alias ");
845: buf->writestring(toChars());
846: buf->writestring(";\n");
847: }
848: }
849:
850:
851: void TypedefDeclaration::toDocBuffer(OutBuffer *buf)
852: {
853: if (ident)
854: {
855: if (isDeprecated())
856: buf->writestring("deprecated ");
857:
858: emitProtection(buf, protection);
859: buf->writestring("typedef ");
860: buf->writestring(toChars());
861: buf->writestring(";\n");
862: }
863: }
864:
865:
866: void FuncDeclaration::toDocBuffer(OutBuffer *buf)
867: {
868: //printf("FuncDeclaration::toDocbuffer() %s\n", toChars());
869: if (ident)
870: {
871: TemplateDeclaration *td;
872:
873: if (parent &&
874: (td = parent->isTemplateDeclaration()) != NULL &&
875: td->onemember == this)
876: { /* It's a function template
877: */
878: unsigned o = buf->offset;
879:
880: declarationToDocBuffer(this, buf, td);
881:
882: highlightCode(NULL, this, buf, o);
883: }
884: else
885: {
886: Declaration::toDocBuffer(buf);
887: }
888: }
889: }
890:
891: #if DMDV1
892: void CtorDeclaration::toDocBuffer(OutBuffer *buf)
893: {
894: HdrGenState hgs;
895:
896: buf->writestring("this");
897: Parameter::argsToCBuffer(buf, &hgs, arguments, varargs);
898: buf->writestring(";\n");
899: }
900: #endif
901:
902: void AggregateDeclaration::toDocBuffer(OutBuffer *buf)
903: {
904: if (ident)
905: {
906: #if 0
907: emitProtection(buf, protection);
908: #endif
909: buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
910: buf->writestring(";\n");
911: }
912: }
913:
914: void StructDeclaration::toDocBuffer(OutBuffer *buf)
915: {
916: //printf("StructDeclaration::toDocbuffer() %s\n", toChars());
917: if (ident)
918: {
919: #if 0
920: emitProtection(buf, protection);
921: #endif
922: TemplateDeclaration *td;
923:
924: if (parent &&
925: (td = parent->isTemplateDeclaration()) != NULL &&
926: td->onemember == this)
927: { unsigned o = buf->offset;
928: td->toDocBuffer(buf);
929: highlightCode(NULL, this, buf, o);
930: }
931: else
932: {
933: buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
934: }
935: buf->writestring(";\n");
936: }
937: }
938:
939: void ClassDeclaration::toDocBuffer(OutBuffer *buf)
940: {
941: //printf("ClassDeclaration::toDocbuffer() %s\n", toChars());
942: if (ident)
943: {
944: #if 0
945: emitProtection(buf, protection);
946: #endif
947: TemplateDeclaration *td;
948:
949: if (parent &&
950: (td = parent->isTemplateDeclaration()) != NULL &&
951: td->onemember == this)
952: { unsigned o = buf->offset;
953: td->toDocBuffer(buf);
954: highlightCode(NULL, this, buf, o);
955: }
956: else
957: {
958: if (isAbstract())
959: buf->writestring("abstract ");
960: buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
961: }
962: int any = 0;
963: for (int i = 0; i < baseclasses->dim; i++)
warning C4018: '<' : signed/unsigned mismatch
964: { BaseClass *bc = baseclasses->tdata()[i];
965:
966: if (bc->protection == PROTprivate)
967: continue;
968: if (bc->base && bc->base->ident == Id::Object)
969: continue;
970:
971: if (any)
972: buf->writestring(", ");
973: else
974: { buf->writestring(": ");
975: any = 1;
976: }
977: emitProtection(buf, bc->protection);
978: if (bc->base)
979: {
980: buf->writestring(bc->base->toPrettyChars());
981: }
982: else
983: {
984: HdrGenState hgs;
985: bc->type->toCBuffer(buf, NULL, &hgs);
986: }
987: }
988: buf->writestring(";\n");
989: }
990: }
991:
992:
993: void EnumDeclaration::toDocBuffer(OutBuffer *buf)
994: {
995: if (ident)
996: {
997: buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
998: buf->writestring(";\n");
999: }
1000: }
1001:
1002: void EnumMember::toDocBuffer(OutBuffer *buf)
1003: {
1004: if (ident)
1005: {
1006: buf->writestring(toChars());
1007: }
1008: }
1009:
1010:
1011: /********************************* DocComment *********************************/
1012:
1013: DocComment::DocComment()
1014: {
1015: memset(this, 0, sizeof(DocComment));
1016: }
1017:
1018: DocComment *DocComment::parse(Scope *sc, Dsymbol *s, unsigned char *comment)
1019: { unsigned idlen;
warning C4101: 'idlen' : unreferenced local variable
1020:
1021: //printf("parse(%s): '%s'\n", s->toChars(), comment);
1022: if (sc->lastdc && isDitto(comment))
1023: return NULL;
1024:
1025: DocComment *dc = new DocComment();
1026: if (!comment)
1027: return dc;
1028:
1029: dc->parseSections(comment);
1030:
1031: for (int i = 0; i < dc->sections.dim; i++)
warning C4018: '<' : signed/unsigned mismatch
1032: { Section *s = dc->sections.tdata()[i];
warning C6246: Local declaration of 's' hides declaration of the same name in outer scope. For additional information, see previous declaration at line '1018' of 'c:\projects\extern\d\dmd\src\doc.c': Lines: 1018
1033:
1034: if (icmp("copyright", s->name, s->namelen) == 0)
1035: {
1036: dc->copyright = s;
1037: }
1038: if (icmp("macros", s->name, s->namelen) == 0)
1039: {
1040: dc->macros = s;
1041: }
1042: }
1043:
1044: sc->lastdc = dc;
1045: return dc;
1046: }
1047:
1048: /*****************************************
1049: * Parse next paragraph out of *pcomment.
1050: * Update *pcomment to point past paragraph.
1051: * Returns NULL if no more paragraphs.
1052: * If paragraph ends in 'identifier:',
1053: * then (*pcomment)[0 .. idlen] is the identifier.
1054: */
1055:
1056: void DocComment::parseSections(unsigned char *comment)
1057: { unsigned char *p;
1058: unsigned char *pstart;
1059: unsigned char *pend;
1060: unsigned char *idstart;
1061: unsigned idlen;
1062:
1063: unsigned char *name = NULL;
1064: unsigned namelen = 0;
1065:
1066: //printf("parseSections('%s')\n", comment);
1067: p = comment;
1068: while (*p)
1069: {
1070: p = skipwhitespace(p);
1071: pstart = p;
1072: pend = p;
1073:
1074: /* Find end of section, which is ended by one of:
1075: * 'identifier:' (but not inside a code section)
1076: * '\0'
1077: */
1078: idlen = 0;
1079: int inCode = 0;
1080: while (1)
1081: {
1082: // Check for start/end of a code section
1083: if (*p == '-')
1084: {
1085: int numdash = 0;
1086: while (*p == '-')
1087: {
1088: ++numdash;
1089: p++;
1090: }
1091: // BUG: handle UTF PS and LS too
1092: if (!*p || *p == '\r' || *p == '\n' && numdash >= 3)
1093: inCode ^= 1;
1094: pend = p;
1095: }
1096:
1097: if (!inCode && isIdStart(p))
1098: {
1099: unsigned char *q = p + utfStride(p);
1100: while (isIdTail(q))
1101: q += utfStride(q);
1102: if (*q == ':') // identifier: ends it
1103: { idlen = q - p;
1104: idstart = p;
1105: for (pend = p; pend > pstart; pend--)
1106: { if (pend[-1] == '\n')
1107: break;
1108: }
1109: p = q + 1;
1110: break;
1111: }
1112: }
1113: while (1)
1114: {
1115: if (!*p)
1116: goto L1;
1117: if (*p == '\n')
1118: { p++;
1119: if (*p == '\n' && !summary && !namelen)
1120: {
1121: pend = p;
1122: p++;
1123: goto L1;
1124: }
1125: break;
1126: }
1127: p++;
1128: pend = p;
1129: }
1130: p = skipwhitespace(p);
1131: }
1132: L1:
1133:
1134: if (namelen || pstart < pend)
1135: {
1136: Section *s;
1137: if (icmp("Params", name, namelen) == 0)
1138: s = new ParamSection();
1139: else if (icmp("Macros", name, namelen) == 0)
1140: s = new MacroSection();
1141: else
1142: s = new Section();
1143: s->name = name;
1144: s->namelen = namelen;
1145: s->body = pstart;
1146: s->bodylen = pend - pstart;
1147: s->nooutput = 0;
1148:
1149: //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
1150:
1151: sections.push(s);
1152:
1153: if (!summary && !namelen)
1154: summary = s;
1155: }
1156:
1157: if (idlen)
1158: { name = idstart;
1159: namelen = idlen;
1160: }
1161: else
1162: { name = NULL;
1163: namelen = 0;
1164: if (!*p)
1165: break;
1166: }
1167: }
1168: }
1169:
1170: void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf)
1171: {
1172: //printf("DocComment::writeSections()\n");
1173: if (sections.dim)
1174: {
1175: buf->writestring("$(DDOC_SECTIONS \n");
1176: for (int i = 0; i < sections.dim; i++)
warning C4018: '<' : signed/unsigned mismatch
1177: { Section *sec = sections.tdata()[i];
1178:
1179: if (sec->nooutput)
1180: continue;
1181: //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
1182: if (sec->namelen || i)
1183: sec->write(this, sc, s, buf);
1184: else
1185: {
1186: buf->writestring("$(DDOC_SUMMARY ");
1187: unsigned o = buf->offset;
1188: buf->write(sec->body, sec->bodylen);
1189: escapeStrayParenthesis(buf, o, s->loc);
1190: highlightText(sc, s, buf, o);
1191: buf->writestring(")\n");
1192: }
1193: }
1194: buf->writestring(")\n");
1195: }
1196: else
1197: {
1198: buf->writestring("$(DDOC_BLANKLINE)\n");
1199: }
1200: }
1201:
1202: /***************************************************
1203: */
1204:
1205: void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
1206: {
1207: if (namelen)
1208: {
1209: static const char *table[] =
1210: { "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
1211: "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
1212: "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
1213: "VERSION" };
1214:
1215: for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++)
1216: {
1217: if (icmp(table[i], name, namelen) == 0)
1218: {
1219: buf->printf("$(DDOC_%s ", table[i]);
1220: goto L1;
1221: }
1222: }
1223:
1224: buf->writestring("$(DDOC_SECTION ");
1225: // Replace _ characters with spaces
1226: buf->writestring("$(DDOC_SECTION_H ");
1227: unsigned o = buf->offset;
1228: for (unsigned u = 0; u < namelen; u++)
1229: { unsigned char c = name[u];
1230: buf->writeByte((c == '_') ? ' ' : c);
1231: }
1232: escapeStrayParenthesis(buf, o, s->loc);
1233: buf->writestring(":)\n");
1234: }
1235: else
1236: {
1237: buf->writestring("$(DDOC_DESCRIPTION ");
1238: }
1239: L1:
1240: unsigned o = buf->offset;
1241: buf->write(body, bodylen);
1242: escapeStrayParenthesis(buf, o, s->loc);
1243: highlightText(sc, s, buf, o);
1244: buf->writestring(")\n");
1245: }
1246:
1247: /***************************************************
1248: */
1249:
1250: void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
1251: {
1252: unsigned char *p = body;
1253: unsigned len = bodylen;
1254: unsigned char *pend = p + len;
1255:
1256: unsigned char *tempstart;
1257: unsigned templen;
1258:
1259: unsigned char *namestart;
1260: unsigned namelen = 0; // !=0 if line continuation
1261:
1262: unsigned char *textstart;
1263: unsigned textlen;
1264:
1265: unsigned o;
1266: Parameter *arg;
1267:
1268: buf->writestring("$(DDOC_PARAMS \n");
1269: while (p < pend)
1270: {
1271: // Skip to start of macro
1272: while (1)
1273: {
1274: switch (*p)
1275: {
1276: case ' ':
1277: case '\t':
1278: p++;
1279: continue;
1280:
1281: case '\n':
1282: p++;
1283: goto Lcont;
1284:
1285: default:
1286: if (isIdStart(p))
1287: break;
1288: if (namelen)
1289: goto Ltext; // continuation of prev macro
1290: goto Lskipline;
1291: }
1292: break;
1293: }
1294: tempstart = p;
1295:
1296: while (isIdTail(p))
1297: p += utfStride(p);
1298: templen = p - tempstart;
1299:
1300: while (*p == ' ' || *p == '\t')
1301: p++;
1302:
1303: if (*p != '=')
1304: { if (namelen)
1305: goto Ltext; // continuation of prev macro
1306: goto Lskipline;
1307: }
1308: p++;
1309:
1310: if (namelen)
1311: { // Output existing param
1312:
1313: L1:
1314: //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1315: HdrGenState hgs;
1316: buf->writestring("$(DDOC_PARAM_ROW ");
1317: buf->writestring("$(DDOC_PARAM_ID ");
1318: o = buf->offset;
1319: arg = isFunctionParameter(s, namestart, namelen);
1320: if (arg && arg->type && arg->ident)
1321: arg->type->toCBuffer(buf, arg->ident, &hgs);
1322: else
1323: buf->write(namestart, namelen);
1324: escapeStrayParenthesis(buf, o, s->loc);
1325: highlightCode(sc, s, buf, o);
1326: buf->writestring(")\n");
1327:
1328: buf->writestring("$(DDOC_PARAM_DESC ");
1329: o = buf->offset;
1330: buf->write(textstart, textlen);
1331: escapeStrayParenthesis(buf, o, s->loc);
1332: highlightText(sc, s, buf, o);
1333: buf->writestring(")");
1334: buf->writestring(")\n");
1335: namelen = 0;
1336: if (p >= pend)
1337: break;
1338: }
1339:
1340: namestart = tempstart;
1341: namelen = templen;
1342:
1343: while (*p == ' ' || *p == '\t')
1344: p++;
1345: textstart = p;
1346:
1347: Ltext:
1348: while (*p != '\n')
1349: p++;
1350: textlen = p - textstart;
1351: p++;
1352:
1353: Lcont:
1354: continue;
1355:
1356: Lskipline:
1357: // Ignore this line
1358: while (*p++ != '\n')
1359: ;
1360: }
1361: if (namelen)
1362: goto L1; // write out last one
1363: buf->writestring(")\n");
1364: }
1365:
1366: /***************************************************
1367: */
1368:
1369: void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
1370: {
1371: //printf("MacroSection::write()\n");
1372: DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen);
1373: }
1374:
1375: /************************************************
1376: * Parse macros out of Macros: section.
1377: * Macros are of the form:
1378: * name1 = value1
1379: *
1380: * name2 = value2
1381: */
1382:
1383: void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen)
1384: {
1385: unsigned char *p = m;
1386: unsigned len = mlen;
1387: unsigned char *pend = p + len;
1388:
1389: unsigned char *tempstart;
1390: unsigned templen;
1391:
1392: unsigned char *namestart;
1393: unsigned namelen = 0; // !=0 if line continuation
1394:
1395: unsigned char *textstart;
1396: unsigned textlen;
1397:
1398: while (p < pend)
1399: {
1400: // Skip to start of macro
1401: while (1)
1402: {
1403: if (p >= pend)
1404: goto Ldone;
1405: switch (*p)
1406: {
1407: case ' ':
1408: case '\t':
1409: p++;
1410: continue;
1411:
1412: case '\n':
1413: p++;
1414: goto Lcont;
1415:
1416: default:
1417: if (isIdStart(p))
1418: break;
1419: if (namelen)
1420: goto Ltext; // continuation of prev macro
1421: goto Lskipline;
1422: }
1423: break;
1424: }
1425: tempstart = p;
1426:
1427: while (1)
1428: {
1429: if (p >= pend)
1430: goto Ldone;
1431: if (!isIdTail(p))
1432: break;
1433: p += utfStride(p);
1434: }
1435: templen = p - tempstart;
1436:
1437: while (1)
1438: {
1439: if (p >= pend)
1440: goto Ldone;
1441: if (!(*p == ' ' || *p == '\t'))
1442: break;
1443: p++;
1444: }
1445:
1446: if (*p != '=')
1447: { if (namelen)
1448: goto Ltext; // continuation of prev macro
1449: goto Lskipline;
1450: }
1451: p++;
1452: if (p >= pend)
1453: goto Ldone;
1454:
1455: if (namelen)
1456: { // Output existing macro
1457: L1:
1458: //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1459: if (icmp("ESCAPES", namestart, namelen) == 0)
1460: parseEscapes(pescapetable, textstart, textlen);
1461: else
1462: Macro::define(pmacrotable, namestart, namelen, textstart, textlen);
1463: namelen = 0;
1464: if (p >= pend)
1465: break;
1466: }
1467:
1468: namestart = tempstart;
1469: namelen = templen;
1470:
1471: while (p < pend && (*p == ' ' || *p == '\t'))
1472: p++;
1473: textstart = p;
1474:
1475: Ltext:
1476: while (p < pend && *p != '\n')
1477: p++;
1478: textlen = p - textstart;
1479:
1480: // Remove trailing \r if there is one
1481: if (p > m && p[-1] == '\r')
1482: textlen--;
1483:
1484: p++;
1485: //printf("p = %p, pend = %p\n", p, pend);
1486:
1487: Lcont:
1488: continue;
1489:
1490: Lskipline:
1491: // Ignore this line
1492: while (p < pend && *p++ != '\n')
1493: ;
1494: }
1495: Ldone:
1496: if (namelen)
1497: goto L1; // write out last one
1498: }
1499:
1500: /**************************************
1501: * Parse escapes of the form:
1502: * /c/string/
1503: * where c is a single character.
1504: * Multiple escapes can be separated
1505: * by whitespace and/or commas.
1506: */
1507:
1508: void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen)
1509: { Escape *escapetable = *pescapetable;
1510:
1511: if (!escapetable)
1512: { escapetable = new Escape;
1513: *pescapetable = escapetable;
1514: }
1515: unsigned char *p = textstart;
1516: unsigned char *pend = p + textlen;
1517:
1518: while (1)
1519: {
1520: while (1)
1521: {
1522: if (p + 4 >= pend)
1523: return;
1524: if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ','))
1525: break;
1526: p++;
1527: }
1528: if (p[0] != '/' || p[2] != '/')
1529: return;
1530: unsigned char c = p[1];
1531: p += 3;
1532: unsigned char *start = p;
1533: while (1)
1534: {
1535: if (p >= pend)
1536: return;
1537: if (*p == '/')
1538: break;
1539: p++;
1540: }
1541: size_t len = p - start;
1542: char *s = (char *)memcpy(mem.malloc(len + 1), start, len);
1543: s[len] = 0;
1544: escapetable->strings[c] = s;
1545: //printf("%c = '%s'\n", c, s);
1546: p++;
1547: }
1548: }
1549:
1550:
1551: /******************************************
1552: * Compare 0-terminated string with length terminated string.
1553: * Return < 0, ==0, > 0
1554: */
1555:
1556: int cmp(const char *stringz, void *s, size_t slen)
1557: {
1558: size_t len1 = strlen(stringz);
1559:
1560: if (len1 != slen)
1561: return len1 - slen;
1562: return memcmp(stringz, s, slen);
1563: }
1564:
1565: int icmp(const char *stringz, void *s, size_t slen)
1566: {
1567: size_t len1 = strlen(stringz);
1568:
1569: if (len1 != slen)
1570: return len1 - slen;
1571: return memicmp(stringz, (char *)s, slen);
warning C4996: 'memicmp': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _memicmp. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\string.h(93) : see declaration of 'memicmp'
1572: }
1573:
1574: /*****************************************
1575: * Return !=0 if comment consists entirely of "ditto".
1576: */
1577:
1578: int isDitto(unsigned char *comment)
1579: {
1580: if (comment)
1581: {
1582: unsigned char *p = skipwhitespace(comment);
1583:
1584: if (memicmp((char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0)
warning C4996: 'memicmp': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _memicmp. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\string.h(93) : see declaration of 'memicmp'
1585: return 1;
1586: }
1587: return 0;
1588: }
1589:
1590: /**********************************************
1591: * Skip white space.
1592: */
1593:
1594: unsigned char *skipwhitespace(unsigned char *p)
1595: {
1596: for (; 1; p++)
1597: { switch (*p)
1598: {
1599: case ' ':
1600: case '\t':
1601: case '\n':
1602: continue;
1603: }
1604: break;
1605: }
1606: return p;
1607: }
1608:
1609:
1610: /************************************************
1611: * Scan forward to one of:
1612: * start of identifier
1613: * beginning of next line
1614: * end of buf
1615: */
1616:
1617: unsigned skiptoident(OutBuffer *buf, size_t i)
1618: {
1619: while (i < buf->offset)
1620: { dchar_t c;
1621:
1622: size_t oi = i;
1623: if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c))
1624: /* Ignore UTF errors, but still consume input
1625: */
1626: break;
1627: if (c >= 0x80)
1628: {
1629: if (!isUniAlpha(c))
1630: continue;
1631: }
1632: else if (!(isalpha(c) || c == '_' || c == '\n'))
1633: continue;
1634: i = oi;
1635: break;
1636: }
1637: return i;
1638: }
1639:
1640: /************************************************
1641: * Scan forward past end of identifier.
1642: */
1643:
1644: unsigned skippastident(OutBuffer *buf, size_t i)
1645: {
1646: while (i < buf->offset)
1647: { dchar_t c;
1648:
1649: size_t oi = i;
1650: if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c))
1651: /* Ignore UTF errors, but still consume input
1652: */
1653: break;
1654: if (c >= 0x80)
1655: {
1656: if (isUniAlpha(c))
1657: continue;
1658: }
1659: else if (isalnum(c) || c == '_')
1660: continue;
1661: i = oi;
1662: break;
1663: }
1664: return i;
1665: }
1666:
1667:
1668: /************************************************
1669: * Scan forward past URL starting at i.
1670: * We don't want to highlight parts of a URL.
1671: * Returns:
1672: * i if not a URL
1673: * index just past it if it is a URL
1674: */
1675:
1676: unsigned skippastURL(OutBuffer *buf, size_t i)
1677: { unsigned length = buf->offset - i;
1678: unsigned char *p = &buf->data[i];
1679: unsigned j;
1680: unsigned sawdot = 0;
1681:
1682: if (length > 7 && memicmp((char *)p, "http://", 7) == 0)
warning C4996: 'memicmp': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _memicmp. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\string.h(93) : see declaration of 'memicmp'
1683: {
1684: j = 7;
1685: }
1686: else if (length > 8 && memicmp((char *)p, "https://", 8) == 0)
warning C4996: 'memicmp': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _memicmp. See online help for details.
c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\string.h(93) : see declaration of 'memicmp'
1687: {
1688: j = 8;
1689: }
1690: else
1691: goto Lno;
1692:
1693: for (; j < length; j++)
1694: { unsigned char c = p[j];
1695: if (isalnum(c))
1696: continue;
1697: if (c == '-' || c == '_' || c == '?' ||
1698: c == '=' || c == '%' || c == '&' ||
1699: c == '/' || c == '+' || c == '#' ||
1700: c == '~')
1701: continue;
1702: if (c == '.')
1703: {
1704: sawdot = 1;
1705: continue;
1706: }
1707: break;
1708: }
1709: if (sawdot)
1710: return i + j;
1711:
1712: Lno:
1713: return i;
1714: }
1715:
1716:
1717: /****************************************************
1718: */
1719:
1720: int isKeyword(unsigned char *p, unsigned len)
1721: {
1722: static const char *table[] = { "true", "false", "null" };
1723:
1724: for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++)
1725: {
1726: if (cmp(table[i], p, len) == 0)
1727: return 1;
1728: }
1729: return 0;
1730: }
1731:
1732: /****************************************************
1733: */
1734:
1735: Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len)
1736: {
1737: FuncDeclaration *f = s->isFuncDeclaration();
1738:
1739: /* f->type may be NULL for template members.
1740: */
1741: if (f && f->type)
1742: {
1743: TypeFunction *tf;
1744: if (f->originalType)
1745: {
1746: tf = (TypeFunction *)f->originalType;
1747: }
1748: else
1749: tf = (TypeFunction *)f->type;
1750:
1751: if (tf->parameters)
1752: {
1753: for (size_t k = 0; k < tf->parameters->dim; k++)
1754: { Parameter *arg = tf->parameters->tdata()[k];
1755:
1756: if (arg->ident && cmp(arg->ident->toChars(), p, len) == 0)
1757: {
1758: return arg;
1759: }
1760: }
1761: }
1762: }
1763: return NULL;
1764: }
1765:
1766: /**************************************************
1767: * Highlight text section.
1768: */
1769:
1770: void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
1771: {
1772: //printf("highlightText()\n");
1773: const char *sid = s->ident->toChars();
1774: FuncDeclaration *f = s->isFuncDeclaration();
1775: unsigned char *p;
1776: const char *se;
1777:
1778: int leadingBlank = 1;
1779: int inCode = 0;
1780: int inComment = 0; // in <!-- ... --> comment
1781: unsigned iCodeStart; // start of code section
1782:
1783: unsigned iLineStart = offset;
1784:
1785: for (unsigned i = offset; i < buf->offset; i++)
1786: { unsigned char c = buf->data[i];
1787:
1788: Lcont:
1789: switch (c)
1790: {
1791: case ' ':
1792: case '\t':
1793: break;
1794:
1795: case '\n':
1796: if (sc && !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n"
1797: {
1798: static char blankline[] = "$(DDOC_BLANKLINE)\n";
1799:
1800: i = buf->insert(i, blankline, sizeof(blankline) - 1);
1801: }
1802: leadingBlank = 1;
1803: iLineStart = i + 1;
1804: break;
1805:
1806: case '<':
1807: leadingBlank = 0;
1808: if (inCode)
1809: break;
1810: p = &buf->data[i];
1811:
1812: // Skip over comments
1813: if (p[1] == '!' && p[2] == '-' && p[3] == '-')
1814: { unsigned j = i + 4;
1815: p += 4;
1816: while (1)
1817: {
1818: if (j == buf->offset)
1819: goto L1;
1820: if (p[0] == '-' && p[1] == '-' && p[2] == '>')
1821: {
1822: i = j + 2; // place on closing '>'
1823: break;
1824: }
1825: j++;
1826: p++;
1827: }
1828: break;
1829: }
1830:
1831: // Skip over HTML tag
1832: if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2])))
1833: { unsigned j = i + 2;
1834: p += 2;
1835: while (1)
1836: {
1837: if (j == buf->offset)
1838: goto L1;
1839: if (p[0] == '>')
1840: {
1841: i = j; // place on closing '>'
1842: break;
1843: }
1844: j++;
1845: p++;
1846: }
1847: break;
1848: }
1849:
1850: L1:
1851: // Replace '<' with '<' character entity
1852: se = Escape::escapeChar('<');
1853: if (se)
1854: { size_t len = strlen(se);
1855: buf->remove(i, 1);
1856: i = buf->insert(i, se, len);
1857: i--; // point to ';'
1858: }
1859: break;
1860:
1861: case '>':
1862: leadingBlank = 0;
1863: if (inCode)
1864: break;
1865: // Replace '>' with '>' character entity
1866: se = Escape::escapeChar('>');
1867: if (se)
1868: { size_t len = strlen(se);
1869: buf->remove(i, 1);
1870: i = buf->insert(i, se, len);
1871: i--; // point to ';'
1872: }
1873: break;
1874:
1875: case '&':
1876: leadingBlank = 0;
1877: if (inCode)
1878: break;
1879: p = &buf->data[i];
1880: if (p[1] == '#' || isalpha(p[1]))
1881: break; // already a character entity
1882: // Replace '&' with '&' character entity
1883: se = Escape::escapeChar('&');
1884: if (se)
1885: { size_t len = strlen(se);
1886: buf->remove(i, 1);
1887: i = buf->insert(i, se, len);
1888: i--; // point to ';'
1889: }
1890: break;
1891:
1892: case '-':
1893: /* A line beginning with --- delimits a code section.
1894: * inCode tells us if it is start or end of a code section.
1895: */
1896: if (leadingBlank)
1897: { int istart = i;
1898: int eollen = 0;
1899:
1900: leadingBlank = 0;
1901: while (1)
1902: {
1903: ++i;
1904: if (i >= buf->offset)
1905: break;
1906: c = buf->data[i];
1907: if (c == '\n')
1908: { eollen = 1;
1909: break;
1910: }
1911: if (c == '\r')
1912: {
1913: eollen = 1;
1914: if (i + 1 >= buf->offset)
1915: break;
1916: if (buf->data[i + 1] == '\n')
1917: { eollen = 2;
1918: break;
1919: }
1920: }
1921: // BUG: handle UTF PS and LS too
1922: if (c != '-')
1923: goto Lcont;
1924: }
1925: if (i - istart < 3)
1926: goto Lcont;
1927:
1928: // We have the start/end of a code section
1929:
1930: // Remove the entire --- line, including blanks and \n
1931: buf->remove(iLineStart, i - iLineStart + eollen);
1932: i = iLineStart;
1933:
1934: if (inCode && (i <= iCodeStart))
1935: { // Empty code section, just remove it completely.
1936: inCode = 0;
1937: break;
1938: }
1939:
1940: if (inCode)
1941: {
1942: inCode = 0;
1943: // The code section is from iCodeStart to i
1944: OutBuffer codebuf;
1945:
1946: codebuf.write(buf->data + iCodeStart, i - iCodeStart);
1947: codebuf.writeByte(0);
1948: highlightCode2(sc, s, &codebuf, 0);
1949: buf->remove(iCodeStart, i - iCodeStart);
1950: i = buf->insert(iCodeStart, codebuf.data, codebuf.offset);
1951: i = buf->insert(i, ")\n", 2);
1952: i--;
1953: }
1954: else
1955: { static char pre[] = "$(D_CODE \n";
1956:
1957: inCode = 1;
1958: i = buf->insert(i, pre, sizeof(pre) - 1);
1959: iCodeStart = i;
1960: i--; // place i on >
1961: leadingBlank = true;
1962: }
1963: }
1964: break;
1965:
1966: default:
1967: leadingBlank = 0;
1968: if (sc && !inCode && isIdStart(&buf->data[i]))
1969: { unsigned j;
1970:
1971: j = skippastident(buf, i);
1972: if (j > i)
1973: {
1974: unsigned k = skippastURL(buf, i);
1975: if (k > i)
1976: { i = k - 1;
1977: break;
1978: }
1979:
1980: if (buf->data[i] == '_') // leading '_' means no highlight
1981: {
1982: buf->remove(i, 1);
1983: i = j - 1;
1984: }
1985: else
1986: {
1987: if (cmp(sid, buf->data + i, j - i) == 0)
1988: {
1989: i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
1990: break;
1991: }
1992: else if (isKeyword(buf->data + i, j - i))
1993: {
1994: i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1;
1995: break;
1996: }
1997: else
1998: {
1999: if (f && isFunctionParameter(f, buf->data + i, j - i))
2000: {
2001: //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2002: i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
2003: break;
2004: }
2005: }
2006: i = j - 1;
2007: }
2008: }
2009: }
2010: break;
2011: }
2012: }
2013: Ldone:
warning C4102: 'Ldone' : unreferenced label
2014: if (inCode)
2015: s->error("unmatched --- in DDoc comment");
2016: ;
2017: }
2018:
2019: /**************************************************
2020: * Highlight code for DDOC section.
2021: */
2022:
2023: void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
2024: {
2025: char *sid = s->ident->toChars();
2026: FuncDeclaration *f = s->isFuncDeclaration();
2027:
2028: //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind());
2029: for (unsigned i = offset; i < buf->offset; i++)
2030: { unsigned char c = buf->data[i];
2031: const char *se;
2032:
2033: se = Escape::escapeChar(c);
2034: if (se)
2035: {
2036: size_t len = strlen(se);
2037: buf->remove(i, 1);
2038: i = buf->insert(i, se, len);
2039: i--; // point to ';'
2040: }
2041: else if (isIdStart(&buf->data[i]))
2042: { unsigned j;
2043:
2044: j = skippastident(buf, i);
2045: if (j > i)
2046: {
2047: if (cmp(sid, buf->data + i, j - i) == 0)
2048: {
2049: i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
2050: continue;
2051: }
2052: else if (f)
2053: {
2054: if (isFunctionParameter(f, buf->data + i, j - i))
2055: {
2056: //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2057: i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
2058: continue;
2059: }
2060: }
2061: i = j - 1;
2062: }
2063: }
2064: }
2065: }
2066:
2067: /****************************************
2068: */
2069:
2070: void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend)
2071: {
2072: for (; p < pend; p++)
2073: { const char *s = Escape::escapeChar(*p);
2074: if (s)
2075: buf->writestring(s);
2076: else
2077: buf->writeByte(*p);
2078: }
2079: }
2080:
2081: /**************************************************
2082: * Highlight code for CODE section.
2083: */
2084:
2085:
2086: void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
2087: {
2088: char *sid = s->ident->toChars();
2089: FuncDeclaration *f = s->isFuncDeclaration();
2090: unsigned errorsave = global.errors;
2091: Lexer lex(NULL, buf->data, 0, buf->offset - 1, 0, 1);
2092: Token tok;
2093: OutBuffer res;
2094: unsigned char *lastp = buf->data;
2095: const char *highlight;
2096:
2097: //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data);
2098: res.reserve(buf->offset);
2099: while (1)
2100: {
2101: lex.scan(&tok);
2102: highlightCode3(&res, lastp, tok.ptr);
2103: highlight = NULL;
2104: switch (tok.value)
2105: {
2106: case TOKidentifier:
2107: if (!sc)
2108: break;
2109: if (cmp(sid, tok.ptr, lex.p - tok.ptr) == 0)
2110: {
2111: highlight = "$(D_PSYMBOL ";
2112: break;
2113: }
2114: else if (f)
2115: {
2116: if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr))
2117: {
2118: //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2119: highlight = "$(D_PARAM ";
2120: break;
2121: }
2122: }
2123: break;
2124:
2125: case TOKcomment:
2126: highlight = "$(D_COMMENT ";
2127: break;
2128:
2129: case TOKstring:
2130: highlight = "$(D_STRING ";
2131: break;
2132:
2133: default:
2134: if (tok.isKeyword())
2135: highlight = "$(D_KEYWORD ";
2136: break;
2137: }
2138: if (highlight)
2139: res.writestring(highlight);
2140: highlightCode3(&res, tok.ptr, lex.p);
2141: if (highlight)
2142: res.writeByte(')');
2143: if (tok.value == TOKeof)
2144: break;
2145: lastp = lex.p;
2146: }
2147: buf->setsize(offset);
2148: buf->write(&res);
2149: global.errors = errorsave;
2150: }
2151:
2152: /***************************************
2153: * Find character string to replace c with.
2154: */
2155:
2156: const char *Escape::escapeChar(unsigned c)
2157: { const char *s;
2158:
2159: switch (c)
2160: {
2161: case '<':
2162: s = "<";
2163: break;
2164: case '>':
2165: s = ">";
2166: break;
2167: case '&':
2168: s = "&";
2169: break;
2170: default:
2171: s = NULL;
2172: break;
2173: }
2174: return s;
2175: }
2176:
2177: /****************************************
2178: * Determine if p points to the start of an identifier.
2179: */
2180:
2181: int isIdStart(unsigned char *p)
2182: {
2183: unsigned c = *p;
2184: if (isalpha(c) || c == '_')
2185: return 1;
2186: if (c >= 0x80)
2187: { size_t i = 0;
2188: if (utf_decodeChar(p, 4, &i, &c))
2189: return 0; // ignore errors
2190: if (isUniAlpha(c))
2191: return 1;
2192: }
2193: return 0;
2194: }
2195:
2196: /****************************************
2197: * Determine if p points to the rest of an identifier.
2198: */
2199:
2200: int isIdTail(unsigned char *p)
2201: {
2202: unsigned c = *p;
2203: if (isalnum(c) || c == '_')
2204: return 1;
2205: if (c >= 0x80)
2206: { size_t i = 0;
2207: if (utf_decodeChar(p, 4, &i, &c))
2208: return 0; // ignore errors
2209: if (isUniAlpha(c))
2210: return 1;
2211: }
2212: return 0;
2213: }
2214:
2215: /*****************************************
2216: * Return number of bytes in UTF character.
2217: */
2218:
2219: int utfStride(unsigned char *p)
2220: {
2221: unsigned c = *p;
2222: if (c < 0x80)
2223: return 1;
2224: size_t i = 0;
2225: utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input
2226: return i;
2227: }
2228: