root/trunk/rebuild/doc.c

Revision 915, 42.9 kB (checked in by Gregor, 3 months ago)

MERGE: dmdfe-2.0 r914 (DMD 2.019)

Line 
1 // Compiler implementation of the D programming language
2 // Copyright (c) 1999-2008 by Digital Mars
3 // All Rights Reserved
4 // written by Walter Bright
5 // http://www.digitalmars.com
6 // License for redistribution is by either the Artistic License
7 // in artistic.txt, or the GNU General Public License in gnu.txt.
8 // See the included readme.txt for details.
9
10 // This implements the Ddoc capability.
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <time.h>
15 #include <ctype.h>
16 #include <assert.h>
17
18 #if IN_GCC || IN_DMDFE
19 #include "mem.h"
20 #else
21 #if _WIN32
22 #include "..\root\mem.h"
23 #elif linux
24 #include "../root/mem.h"
25 #else
26 #error "fix this"
27 #endif
28 #endif
29
30 #include "root.h"
31
32 #include "mars.h"
33 #include "dsymbol.h"
34 #include "macro.h"
35 #include "template.h"
36 #include "lexer.h"
37 #include "aggregate.h"
38 #include "declaration.h"
39 #include "enum.h"
40 #include "id.h"
41 #include "module.h"
42 #include "scope.h"
43 #include "hdrgen.h"
44 #include "doc.h"
45 #include "mtype.h"
46
47 struct Escape
48 {
49     const char *strings[256];
50
51     static const char *escapeChar(unsigned c);
52 };
53
54 struct Section
55 {
56     unsigned char *name;
57     unsigned namelen;
58
59     unsigned char *body;
60     unsigned bodylen;
61
62     int nooutput;
63
64     virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
65 };
66
67 struct ParamSection : Section
68 {
69     void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
70 };
71
72 struct MacroSection : Section
73 {
74     void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
75 };
76
77 struct DocComment
78 {
79     Array sections;     // Section*[]
80
81     Section *summary;
82     Section *copyright;
83     Section *macros;
84     Macro **pmacrotable;
85     Escape **pescapetable;
86
87     DocComment();
88
89     static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment);
90     static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen);
91     static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen);
92
93     void parseSections(unsigned char *comment);
94     void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf);
95 };
96
97
98 int cmp(const char *stringz, void *s, size_t slen);
99 int icmp(const char *stringz, void *s, size_t slen);
100 int isDitto(unsigned char *comment);
101 unsigned char *skipwhitespace(unsigned char *p);
102 unsigned skiptoident(OutBuffer *buf, unsigned i);
103 unsigned skippastident(OutBuffer *buf, unsigned i);
104 unsigned skippastURL(OutBuffer *buf, unsigned i);
105 void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
106 void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
107 void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
108 Argument *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len);
109
110 static unsigned char ddoc_default[] = "\
111 DDOC =  <html><head>\n\
112     <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
113     <title>$(TITLE)</title>\n\
114     </head><body>\n\
115     <h1>$(TITLE)</h1>\n\
116     $(BODY)\n\
117     <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\
118     </body></html>\n\
119 \n\
120 B = <b>$0</b>\n\
121 I = <i>$0</i>\n\
122 U = <u>$0</u>\n\
123 P = <p>$0</p>\n\
124 DL =    <dl>$0</dl>\n\
125 DT =    <dt>$0</dt>\n\
126 DD =    <dd>$0</dd>\n\
127 TABLE = <table>$0</table>\n\
128 TR =    <tr>$0</tr>\n\
129 TH =    <th>$0</th>\n\
130 TD =    <td>$0</td>\n\
131 OL =    <ol>$0</ol>\n\
132 UL =    <ul>$0</ul>\n\
133 LI =    <li>$0</li>\n\
134 BIG =   <big>$0</big>\n\
135 SMALL = <small>$0</small>\n\
136 BR =    <br>\n\
137 LINK =  <a href=\"$0\">$0</a>\n\
138 LINK2 = <a href=\"$1\">$+</a>\n\
139 \n\
140 RED =   <font color=red>$0</font>\n\
141 BLUE =  <font color=blue>$0</font>\n\
142 GREEN = <font color=green>$0</font>\n\
143 YELLOW =<font color=yellow>$0</font>\n\
144 BLACK = <font color=black>$0</font>\n\
145 WHITE = <font color=white>$0</font>\n\
146 \n\
147 D_CODE = <pre class=\"d_code\">$0</pre>\n\
148 D_COMMENT = $(GREEN $0)\n\
149 D_STRING  = $(RED $0)\n\
150 D_KEYWORD = $(BLUE $0)\n\
151 D_PSYMBOL = $(U $0)\n\
152 D_PARAM   = $(I $0)\n\
153 \n\
154 DDOC_COMMENT   = <!-- $0 -->\n\
155 DDOC_DECL      = $(DT $(BIG $0))\n\
156 DDOC_DECL_DD   = $(DD $0)\n\
157 DDOC_DITTO     = $(BR)$0\n\
158 DDOC_SECTIONS  = $0\n\
159 DDOC_SUMMARY   = $0$(BR)$(BR)\n\
160 DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
161 DDOC_AUTHORS   = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
162 DDOC_BUGS      = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
163 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
164 DDOC_DATE      = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
165 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
166 DDOC_EXAMPLES  = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
167 DDOC_HISTORY   = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
168 DDOC_LICENSE   = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
169 DDOC_RETURNS   = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
170 DDOC_SEE_ALSO  = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
171 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
172 DDOC_THROWS    = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
173 DDOC_VERSION   = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
174 DDOC_SECTION_H = $(B $0)$(BR)\n\
175 DDOC_SECTION   = $0$(BR)$(BR)\n\
176 DDOC_MEMBERS   = $(DL $0)\n\
177 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
178 DDOC_CLASS_MEMBERS  = $(DDOC_MEMBERS $0)\n\
179 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
180 DDOC_ENUM_MEMBERS   = $(DDOC_MEMBERS $0)\n\
181 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
182 DDOC_PARAMS    = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
183 DDOC_PARAM_ROW = $(TR $0)\n\
184 DDOC_PARAM_ID  = $(TD $0)\n\
185 DDOC_PARAM_DESC = $(TD $0)\n\
186 DDOC_BLANKLINE  = $(BR)$(BR)\n\
187 \n\
188 DDOC_PSYMBOL    = $(U $0)\n\
189 DDOC_KEYWORD    = $(B $0)\n\
190 DDOC_PARAM  = $(I $0)\n\
191 \n\
192 ESCAPES = /</&lt;/\n\
193       />/&gt;/\n\
194       /&/&amp;/\n\
195 ";
196
197 static char ddoc_decl_s[] = "$(DDOC_DECL ";
198 static char ddoc_decl_e[] = ")\n";
199
200 static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
201 static char ddoc_decl_dd_e[] = ")\n";
202
203
204 /****************************************************
205  */
206
207 void Module::gendocfile()
208 {
209     static OutBuffer mbuf;
210     static int mbuf_done;
211
212     OutBuffer buf;
213
214     //printf("Module::gendocfile()\n");
215
216     if (!mbuf_done)     // if not already read the ddoc files
217     {   mbuf_done = 1;
218
219     // Use our internal default
220     mbuf.write(ddoc_default, sizeof(ddoc_default) - 1);
221
222     // Override with DDOCFILE specified in the sc.ini file
223     char *p = getenv("DDOCFILE");
224     if (p)
225         global.params.ddocfiles->shift(p);
226
227     // Override with the ddoc macro files from the command line
228     for (int i = 0; i < global.params.ddocfiles->dim; i++)
229     {
230         FileName f((char *)global.params.ddocfiles->data[i], 0);
231         File file(&f);
232         file.readv();
233         // BUG: convert file contents to UTF-8 before use
234
235         //printf("file: '%.*s'\n", file.len, file.buffer);
236         mbuf.write(file.buffer, file.len);
237     }
238     }
239     DocComment::parseMacros(&escapetable, &macrotable, mbuf.data, mbuf.offset);
240
241     Scope *sc = Scope::createGlobal(this);  // create root scope
242     sc->docbuf = &buf;
243
244     DocComment *dc = DocComment::parse(sc, this, comment);
245     dc->pmacrotable = &macrotable;
246     dc->pescapetable = &escapetable;
247
248     // Generate predefined macros
249
250     // Set the title to be the name of the module
251     {   char *p = toPrettyChars();
252     Macro::define(&macrotable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p));
253     }
254
255     time_t t;
256     time(&t);
257     char *p = ctime(&t);
258     p = mem.strdup(p);
259     Macro::define(&macrotable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p));
260     Macro::define(&macrotable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4);
261
262     char *docfilename = docfile->toChars();
263     Macro::define(&macrotable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename));
264
265     if (dc->copyright)
266     {
267     dc->copyright->nooutput = 1;
268     Macro::define(&macrotable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
269     }
270
271     buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
272     if (isDocFile)
273     {
274     size_t commentlen = strlen((char *)comment);
275     if (dc->macros)
276     {
277         commentlen = dc->macros->name - comment;
278         dc->macros->write(dc, sc, this, sc->docbuf);
279     }
280     sc->docbuf->write(comment, commentlen);
281     highlightText(NULL, this, sc->docbuf, 0);
282     }
283     else
284     {
285     dc->writeSections(sc, this, sc->docbuf);
286     emitMemberComments(sc);
287     }
288
289     //printf("BODY= '%.*s'\n", buf.offset, buf.data);
290     Macro::define(&macrotable, (unsigned char *)"BODY", 4, buf.data, buf.offset);
291
292     OutBuffer buf2;
293     buf2.writestring("$(DDOC)\n");
294     unsigned end = buf2.offset;
295     macrotable->expand(&buf2, 0, &end, NULL, 0);
296
297 #if 1
298     /* Remove all the escape sequences from buf2,
299      * and make CR-LF the newline.
300      */
301     {
302     buf.setsize(0);
303     buf.reserve(buf2.offset);
304     unsigned char *p = buf2.data;
305     for (unsigned j = 0; j < buf2.offset; j++)
306     {
307         unsigned char c = p[j];
308         if (c == 0xFF && j + 1 < buf2.offset)
309         {
310         j++;
311         continue;
312         }
313         if (c == '\n')
314         buf.writeByte('\r');
315         else if (c == '\r')
316         {
317         buf.writestring("\r\n");
318         if (j + 1 < buf2.offset && p[j + 1] == '\n')
319         {
320             j++;
321         }
322         continue;
323         }
324         buf.writeByte(c);
325     }
326     }
327
328     // Transfer image to file
329     assert(docfile);
330     docfile->setbuffer(buf.data, buf.offset);
331     docfile->ref = 1;
332     char *pt = FileName::path(docfile->toChars());
333     if (*pt)
334     FileName::ensurePathExists(pt);
335     mem.free(pt);
336     docfile->writev();
337 #else
338     /* Remove all the escape sequences from buf2
339      */
340     {   unsigned i = 0;
341     unsigned char *p = buf2.data;
342     for (unsigned j = 0; j < buf2.offset; j++)
343     {
344         if (p[j] == 0xFF && j + 1 < buf2.offset)
345         {
346         j++;
347         continue;
348         }
349         p[i] = p[j];
350         i++;
351     }
352     buf2.setsize(i);
353     }
354
355     // Transfer image to file
356     docfile->setbuffer(buf2.data, buf2.offset);
357     docfile->ref = 1;
358     char *pt = FileName::path(docfile->toChars());
359     if (*pt)
360     FileName::ensurePathExists(pt);
361     mem.free(pt);
362     docfile->writev();
363 #endif
364 }
365
366 /******************************* emitComment **********************************/
367
368 /*
369  * Emit doc comment to documentation file
370  */
371
372 void Dsymbol::emitDitto(Scope *sc)
373 {
374     //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars());
375     OutBuffer *buf = sc->docbuf;
376     unsigned o;
377     OutBuffer b;
378
379     b.writestring("$(DDOC_DITTO ");
380     o = b.offset;
381     toDocBuffer(&b);
382     //printf("b: '%.*s'\n", b.offset, b.data);
383     /* If 'this' is a function template, then highlightCode() was
384      * already run by FuncDeclaration::toDocbuffer().
385      */
386     TemplateDeclaration *td;
387     if (parent &&
388         (td = parent->isTemplateDeclaration()) != NULL &&
389         td->onemember == this)
390     {
391     }
392     else
393         highlightCode(sc, this, &b, o);
394     b.writeByte(')');
395     buf->spread(sc->lastoffset, b.offset);
396     memcpy(buf->data + sc->lastoffset, b.data, b.offset);
397     sc->lastoffset += b.offset;
398 }
399
400 void ScopeDsymbol::emitMemberComments(Scope *sc)
401 {
402     //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
403     OutBuffer *buf = sc->docbuf;
404
405     if (members)
406     {   const char *m = "$(DDOC_MEMBERS \n";
407
408     if (isModule())
409         m = "$(DDOC_MODULE_MEMBERS \n";
410     else if (isClassDeclaration())
411         m = "$(DDOC_CLASS_MEMBERS \n";
412     else if (isStructDeclaration())
413         m = "$(DDOC_STRUCT_MEMBERS \n";
414     else if (isEnumDeclaration())
415         m = "$(DDOC_ENUM_MEMBERS \n";
416     else if (isTemplateDeclaration())
417         m = "$(DDOC_TEMPLATE_MEMBERS \n";
418
419     unsigned offset1 = buf->offset;     // save starting offset
420     buf->writestring(m);
421     unsigned offset2 = buf->offset;     // to see if we write anything
422     sc = sc->push(this);
423     for (int i = 0; i < members->dim; i++)
424     {
425         Dsymbol *s = (Dsymbol *)members->data[i];
426         //printf("\ts = '%s'\n", s->toChars());
427         s->emitComment(sc);
428     }
429     sc->pop();
430     if (buf->offset == offset2)
431     {
432         /* Didn't write out any members, so back out last write
433          */
434         buf->offset = offset1;
435     }
436     else
437         buf->writestring(")\n");
438     }
439 }
440
441 void emitProtection(OutBuffer *buf, PROT prot)
442 {
443     const char *p;
444
445     switch (prot)
446     {
447     case PROTpackage:   p = "package";   break;
448     case PROTprotected: p = "protected"; break;
449     case PROTexport:    p = "export";    break;
450     default:        p = NULL;    break;
451     }
452     if (p)
453     buf->printf("%s ", p);
454 }
455
456 void Dsymbol::emitComment(Scope *sc)           { }
457 void InvariantDeclaration::emitComment(Scope *sc)  { }
458 #if V2
459 void PostBlitDeclaration::emitComment(Scope *sc)   { }
460 #endif
461 void DtorDeclaration::emitComment(Scope *sc)       { }
462 void StaticCtorDeclaration::emitComment(Scope *sc) { }
463 void StaticDtorDeclaration::emitComment(Scope *sc) { }
464 void ClassInfoDeclaration::emitComment(Scope *sc)  { }
465 void ModuleInfoDeclaration::emitComment(Scope *sc) { }
466 void TypeInfoDeclaration::emitComment(Scope *sc)   { }
467
468
469 void Declaration::emitComment(Scope *sc)
470 {
471     //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
472     //printf("type = %p\n", type);
473
474     if (protection == PROTprivate || !ident ||
475     (!type && !isCtorDeclaration() && !isAliasDeclaration()))
476     return;
477     if (!comment)
478     return;
479
480     OutBuffer *buf = sc->docbuf;
481     DocComment *dc = DocComment::parse(sc, this, comment);
482     unsigned o;
483
484     if (!dc)
485     {
486     emitDitto(sc);
487     return;
488     }
489     dc->pmacrotable = &sc->module->macrotable;
490
491     buf->writestring(ddoc_decl_s);
492     o = buf->offset;
493     toDocBuffer(buf);
494     highlightCode(sc, this, buf, o);
495     sc->lastoffset = buf->offset;
496     buf->writestring(ddoc_decl_e);
497
498     buf->writestring(ddoc_decl_dd_s);
499     dc->writeSections(sc, this, buf);
500     buf->writestring(ddoc_decl_dd_e);
501 }
502
503 void AggregateDeclaration::emitComment(Scope *sc)
504 {
505     //printf("AggregateDeclaration::emitComment() '%s'\n", toChars());
506     if (prot() == PROTprivate)
507     return;
508     if (!comment)
509     return;
510
511     OutBuffer *buf = sc->docbuf;
512     DocComment *dc = DocComment::parse(sc, this, comment);
513
514     if (!dc)
515     {
516     emitDitto(sc);
517     return;
518     }
519     dc->pmacrotable = &sc->module->macrotable;
520
521     buf->writestring(ddoc_decl_s);
522     toDocBuffer(buf);
523     sc->lastoffset = buf->offset;
524     buf->writestring(ddoc_decl_e);
525
526     buf->writestring(ddoc_decl_dd_s);
527     dc->writeSections(sc, this, buf);
528     emitMemberComments(sc);
529     buf->writestring(ddoc_decl_dd_e);
530 }
531
532 void TemplateDeclaration::emitComment(Scope *sc)
533 {
534     //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind());
535     if (prot() == PROTprivate)
536     return;
537
538     unsigned char *com = comment;
539     int hasmembers = 1;
540
541     Dsymbol *ss = this;
542
543     if (onemember)
544     {
545     ss = onemember->isAggregateDeclaration();
546     if (!ss)
547     {
548         ss = onemember->isFuncDeclaration();
549         if (ss)
550         {   hasmembers = 0;
551         if (com != ss->comment)
552             com = Lexer::combineComments(com, ss->comment);
553         }
554         else
555         ss = this;
556     }
557     }
558
559     if (!com)
560     return;
561
562     OutBuffer *buf = sc->docbuf;
563     DocComment *dc = DocComment::parse(sc, this, com);
564     unsigned o;
565
566     if (!dc)
567     {
568     ss->emitDitto(sc);
569     return;
570     }
571     dc->pmacrotable = &sc->module->macrotable;
572
573     buf->writestring(ddoc_decl_s);
574     o = buf->offset;
575     ss->toDocBuffer(buf);
576     if (ss == this)
577         highlightCode(sc, this, buf, o);
578     sc->lastoffset = buf->offset;
579     buf->writestring(ddoc_decl_e);
580
581     buf->writestring(ddoc_decl_dd_s);
582     dc->writeSections(sc, this, buf);
583     if (hasmembers)
584     ((ScopeDsymbol *)ss)->emitMemberComments(sc);
585     buf->writestring(ddoc_decl_dd_e);
586 }
587
588 void EnumDeclaration::emitComment(Scope *sc)
589 {
590     if (prot() == PROTprivate)
591     return;
592 //    if (!comment)
593     {   if (isAnonymous() && members)
594     {
595         for (int i = 0; i < members->dim; i++)
596         {
597         Dsymbol *s = (Dsymbol *)members->data[i];
598         s->emitComment(sc);
599         }
600         return;
601     }
602     }
603     if (!comment)
604     return;
605     if (isAnonymous())
606     return;
607
608     OutBuffer *buf = sc->docbuf;
609     DocComment *dc = DocComment::parse(sc, this, comment);
610
611     if (!dc)
612     {
613     emitDitto(sc);
614     return;
615     }
616     dc->pmacrotable = &sc->module->macrotable;
617
618     buf->writestring(ddoc_decl_s);
619     toDocBuffer(buf);
620     sc->lastoffset = buf->offset;
621     buf->writestring(ddoc_decl_e);
622
623     buf