Mercurial > projects > ldc
view dmd/doc.c @ 1288:e109e4031e8a
Fix build when USE_METADATA is off.
author | Matti Niemenmaa <matti.niemenmaa+hg@iki.fi> |
---|---|
date | Sat, 02 May 2009 19:03:33 +0300 |
parents | eeb8b95ea92e |
children | def7a1d494fd |
line wrap: on
line source
// Compiler implementation of the D programming language // Copyright (c) 1999-2008 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. // This implements the Ddoc capability. #include <stdio.h> #include <string.h> #include <time.h> #include <ctype.h> #include <assert.h> #include "rmem.h" #include "root.h" #include "mars.h" #include "dsymbol.h" #include "macro.h" #include "template.h" #include "lexer.h" #include "aggregate.h" #include "declaration.h" #include "enum.h" #include "id.h" #include "module.h" #include "scope.h" #include "hdrgen.h" #include "doc.h" #include "mtype.h" struct Escape { const char *strings[256]; static const char *escapeChar(unsigned c); }; struct Section { unsigned char *name; unsigned namelen; unsigned char *body; unsigned bodylen; int nooutput; virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); }; struct ParamSection : Section { void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); }; struct MacroSection : Section { void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); }; struct DocComment { Array sections; // Section*[] Section *summary; Section *copyright; Section *macros; Macro **pmacrotable; Escape **pescapetable; DocComment(); static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment); static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen); static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen); void parseSections(unsigned char *comment); void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf); }; int cmp(const char *stringz, void *s, size_t slen); int icmp(const char *stringz, void *s, size_t slen); int isDitto(unsigned char *comment); unsigned char *skipwhitespace(unsigned char *p); unsigned skiptoident(OutBuffer *buf, unsigned i); unsigned skippastident(OutBuffer *buf, unsigned i); unsigned skippastURL(OutBuffer *buf, unsigned i); void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); Argument *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len); static unsigned char ddoc_default[] = "\ DDOC = <html><head>\n\ <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\ <title>$(TITLE)</title>\n\ </head><body>\n\ <h1>$(TITLE)</h1>\n\ $(BODY)\n\ <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/1.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\ </body></html>\n\ \n\ B = <b>$0</b>\n\ I = <i>$0</i>\n\ U = <u>$0</u>\n\ P = <p>$0</p>\n\ DL = <dl>$0</dl>\n\ DT = <dt>$0</dt>\n\ DD = <dd>$0</dd>\n\ TABLE = <table>$0</table>\n\ TR = <tr>$0</tr>\n\ TH = <th>$0</th>\n\ TD = <td>$0</td>\n\ OL = <ol>$0</ol>\n\ UL = <ul>$0</ul>\n\ LI = <li>$0</li>\n\ BIG = <big>$0</big>\n\ SMALL = <small>$0</small>\n\ BR = <br>\n\ LINK = <a href=\"$0\">$0</a>\n\ LINK2 = <a href=\"$1\">$+</a>\n\ \n\ RED = <font color=red>$0</font>\n\ BLUE = <font color=blue>$0</font>\n\ GREEN = <font color=green>$0</font>\n\ YELLOW =<font color=yellow>$0</font>\n\ BLACK = <font color=black>$0</font>\n\ WHITE = <font color=white>$0</font>\n\ \n\ D_CODE = <pre class=\"d_code\">$0</pre>\n\ D_COMMENT = $(GREEN $0)\n\ D_STRING = $(RED $0)\n\ D_KEYWORD = $(BLUE $0)\n\ D_PSYMBOL = $(U $0)\n\ D_PARAM = $(I $0)\n\ \n\ DDOC_COMMENT = <!-- $0 -->\n\ DDOC_DECL = $(DT $(BIG $0))\n\ DDOC_DECL_DD = $(DD $0)\n\ DDOC_DITTO = $(BR)$0\n\ DDOC_SECTIONS = $0\n\ DDOC_SUMMARY = $0$(BR)$(BR)\n\ DDOC_DESCRIPTION = $0$(BR)$(BR)\n\ DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\ DDOC_SECTION_H = $(B $0)$(BR)\n\ DDOC_SECTION = $0$(BR)$(BR)\n\ DDOC_MEMBERS = $(DL $0)\n\ DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\ DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\ DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\ DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\ DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\ DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\ DDOC_PARAM_ROW = $(TR $0)\n\ DDOC_PARAM_ID = $(TD $0)\n\ DDOC_PARAM_DESC = $(TD $0)\n\ DDOC_BLANKLINE = $(BR)$(BR)\n\ \n\ DDOC_PSYMBOL = $(U $0)\n\ DDOC_KEYWORD = $(B $0)\n\ DDOC_PARAM = $(I $0)\n\ \n\ ESCAPES = /</</\n\ />/>/\n\ /&/&/\n\ "; static char ddoc_decl_s[] = "$(DDOC_DECL "; static char ddoc_decl_e[] = ")\n"; static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD "; static char ddoc_decl_dd_e[] = ")\n"; /**************************************************** */ void Module::gendocfile() { static OutBuffer mbuf; static int mbuf_done; OutBuffer buf; //printf("Module::gendocfile()\n"); if (!mbuf_done) // if not already read the ddoc files { mbuf_done = 1; // Use our internal default mbuf.write(ddoc_default, sizeof(ddoc_default) - 1); // Override with DDOCFILE specified in the sc.ini file char *p = getenv("DDOCFILE"); if (p) global.params.ddocfiles->shift(p); // Override with the ddoc macro files from the command line for (int i = 0; i < global.params.ddocfiles->dim; i++) { FileName f((char *)global.params.ddocfiles->data[i], 0); File file(&f); file.readv(); // BUG: convert file contents to UTF-8 before use //printf("file: '%.*s'\n", file.len, file.buffer); mbuf.write(file.buffer, file.len); } } DocComment::parseMacros(&escapetable, ¯otable, mbuf.data, mbuf.offset); Scope *sc = Scope::createGlobal(this); // create root scope sc->docbuf = &buf; DocComment *dc = DocComment::parse(sc, this, comment); dc->pmacrotable = ¯otable; dc->pescapetable = &escapetable; // Generate predefined macros // Set the title to be the name of the module { char *p = toPrettyChars(); Macro::define(¯otable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p)); } time_t t; time(&t); char *p = ctime(&t); p = mem.strdup(p); Macro::define(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p)); Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4); char *docfilename = docfile->toChars(); Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename)); if (dc->copyright) { dc->copyright->nooutput = 1; Macro::define(¯otable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); } buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars()); if (isDocFile) { size_t commentlen = strlen((char *)comment); if (dc->macros) { commentlen = dc->macros->name - comment; dc->macros->write(dc, sc, this, sc->docbuf); } sc->docbuf->write(comment, commentlen); highlightText(NULL, this, sc->docbuf, 0); } else { dc->writeSections(sc, this, sc->docbuf); emitMemberComments(sc); } //printf("BODY= '%.*s'\n", buf.offset, buf.data); Macro::define(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset); OutBuffer buf2; buf2.writestring("$(DDOC)\n"); unsigned end = buf2.offset; macrotable->expand(&buf2, 0, &end, NULL, 0); #if 1 /* Remove all the escape sequences from buf2, * and make CR-LF the newline. */ { buf.setsize(0); buf.reserve(buf2.offset); unsigned char *p = buf2.data; for (unsigned j = 0; j < buf2.offset; j++) { unsigned char c = p[j]; if (c == 0xFF && j + 1 < buf2.offset) { j++; continue; } if (c == '\n') buf.writeByte('\r'); else if (c == '\r') { buf.writestring("\r\n"); if (j + 1 < buf2.offset && p[j + 1] == '\n') { j++; } continue; } buf.writeByte(c); } } // Transfer image to file assert(docfile); docfile->setbuffer(buf.data, buf.offset); docfile->ref = 1; char *pt = FileName::path(docfile->toChars()); if (*pt) FileName::ensurePathExists(pt); mem.free(pt); docfile->writev(); #else /* Remove all the escape sequences from buf2 */ { unsigned i = 0; unsigned char *p = buf2.data; for (unsigned j = 0; j < buf2.offset; j++) { if (p[j] == 0xFF && j + 1 < buf2.offset) { j++; continue; } p[i] = p[j]; i++; } buf2.setsize(i); } // Transfer image to file docfile->setbuffer(buf2.data, buf2.offset); docfile->ref = 1; char *pt = FileName::path(docfile->toChars()); if (*pt) FileName::ensurePathExists(pt); mem.free(pt); docfile->writev(); #endif } /******************************* emitComment **********************************/ /* * Emit doc comment to documentation file */ void Dsymbol::emitDitto(Scope *sc) { //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars()); OutBuffer *buf = sc->docbuf; unsigned o; OutBuffer b; b.writestring("$(DDOC_DITTO "); o = b.offset; toDocBuffer(&b); //printf("b: '%.*s'\n", b.offset, b.data); /* If 'this' is a function template, then highlightCode() was * already run by FuncDeclaration::toDocbuffer(). */ TemplateDeclaration *td; if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) { } else highlightCode(sc, this, &b, o); b.writeByte(')'); buf->spread(sc->lastoffset, b.offset); memcpy(buf->data + sc->lastoffset, b.data, b.offset); sc->lastoffset += b.offset; } void ScopeDsymbol::emitMemberComments(Scope *sc) { //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars()); OutBuffer *buf = sc->docbuf; if (members) { const char *m = "$(DDOC_MEMBERS \n"; if (isModule()) m = "$(DDOC_MODULE_MEMBERS \n"; else if (isClassDeclaration()) m = "$(DDOC_CLASS_MEMBERS \n"; else if (isStructDeclaration()) m = "$(DDOC_STRUCT_MEMBERS \n"; else if (isEnumDeclaration()) m = "$(DDOC_ENUM_MEMBERS \n"; else if (isTemplateDeclaration()) m = "$(DDOC_TEMPLATE_MEMBERS \n"; unsigned offset1 = buf->offset; // save starting offset buf->writestring(m); unsigned offset2 = buf->offset; // to see if we write anything sc = sc->push(this); for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("\ts = '%s'\n", s->toChars()); s->emitComment(sc); } sc->pop(); if (buf->offset == offset2) { /* Didn't write out any members, so back out last write */ buf->offset = offset1; } else buf->writestring(")\n"); } } void emitProtection(OutBuffer *buf, PROT prot) { const char *p; switch (prot) { case PROTpackage: p = "package"; break; case PROTprotected: p = "protected"; break; case PROTexport: p = "export"; break; default: p = NULL; break; } if (p) buf->printf("%s ", p); } void Dsymbol::emitComment(Scope *sc) { } void InvariantDeclaration::emitComment(Scope *sc) { } #if DMDV2 void PostBlitDeclaration::emitComment(Scope *sc) { } #endif void DtorDeclaration::emitComment(Scope *sc) { } void StaticCtorDeclaration::emitComment(Scope *sc) { } void StaticDtorDeclaration::emitComment(Scope *sc) { } void ClassInfoDeclaration::emitComment(Scope *sc) { } void ModuleInfoDeclaration::emitComment(Scope *sc) { } void TypeInfoDeclaration::emitComment(Scope *sc) { } void Declaration::emitComment(Scope *sc) { //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); //printf("type = %p\n", type); if (protection == PROTprivate || !ident || (!type && !isCtorDeclaration() && !isAliasDeclaration())) return; if (!comment) return; OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); unsigned o; if (!dc) { emitDitto(sc); return; } dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); o = buf->offset; toDocBuffer(buf); highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); buf->writestring(ddoc_decl_dd_s); dc->writeSections(sc, this, buf); buf->writestring(ddoc_decl_dd_e); } void AggregateDeclaration::emitComment(Scope *sc) { //printf("AggregateDeclaration::emitComment() '%s'\n", toChars()); if (prot() == PROTprivate) return; if (!comment) return; OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); if (!dc) { emitDitto(sc); return; } dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); toDocBuffer(buf); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); buf->writestring(ddoc_decl_dd_s); dc->writeSections(sc, this, buf); emitMemberComments(sc); buf->writestring(ddoc_decl_dd_e); } void TemplateDeclaration::emitComment(Scope *sc) { //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind()); if (prot() == PROTprivate) return; unsigned char *com = comment; int hasmembers = 1; Dsymbol *ss = this; if (onemember) { ss = onemember->isAggregateDeclaration(); if (!ss) { ss = onemember->isFuncDeclaration(); if (ss) { hasmembers = 0; if (com != ss->comment) com = Lexer::combineComments(com, ss->comment); } else ss = this; } } if (!com) return; OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, com); unsigned o; if (!dc) { ss->emitDitto(sc); return; } dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); o = buf->offset; ss->toDocBuffer(buf); if (ss == this) highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); buf->writestring(ddoc_decl_dd_s); dc->writeSections(sc, this, buf); if (hasmembers) ((ScopeDsymbol *)ss)->emitMemberComments(sc); buf->writestring(ddoc_decl_dd_e); } void EnumDeclaration::emitComment(Scope *sc) { if (prot() == PROTprivate) return; // if (!comment) { if (isAnonymous() && members) { for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->emitComment(sc); } return; } } if (!comment) return; if (isAnonymous()) return; OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); if (!dc) { emitDitto(sc); return; } dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); toDocBuffer(buf); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); buf->writestring(ddoc_decl_dd_s); dc->writeSections(sc, this, buf); emitMemberComments(sc); buf->writestring(ddoc_decl_dd_e); } void EnumMember::emitComment(Scope *sc) { //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); if (prot() == PROTprivate) return; if (!comment) return; OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); unsigned o; if (!dc) { emitDitto(sc); return; } dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); o = buf->offset; toDocBuffer(buf); highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); buf->writestring(ddoc_decl_dd_s); dc->writeSections(sc, this, buf); buf->writestring(ddoc_decl_dd_e); } /******************************* toDocBuffer **********************************/ void Dsymbol::toDocBuffer(OutBuffer *buf) { //printf("Dsymbol::toDocbuffer() %s\n", toChars()); HdrGenState hgs; hgs.ddoc = 1; toCBuffer(buf, &hgs); } void prefix(OutBuffer *buf, Dsymbol *s) { if (s->isDeprecated()) buf->writestring("deprecated "); Declaration *d = s->isDeclaration(); if (d) { emitProtection(buf, d->protection); if (d->isAbstract()) buf->writestring("abstract "); if (d->isStatic()) buf->writestring("static "); if (d->isConst()) buf->writestring("const "); #if DMDV2 if (d->isInvariant()) buf->writestring("invariant "); #endif if (d->isFinal()) buf->writestring("final "); if (d->isSynchronized()) buf->writestring("synchronized "); } } void Declaration::toDocBuffer(OutBuffer *buf) { //printf("Declaration::toDocbuffer() %s, originalType = %p\n", toChars(), originalType); if (ident) { prefix(buf, this); if (type) { HdrGenState hgs; hgs.ddoc = 1; if (originalType) { //originalType->print(); originalType->toCBuffer(buf, ident, &hgs); } else type->toCBuffer(buf, ident, &hgs); } else buf->writestring(ident->toChars()); buf->writestring(";\n"); } } void AliasDeclaration::toDocBuffer(OutBuffer *buf) { //printf("AliasDeclaration::toDocbuffer() %s\n", toChars()); if (ident) { if (isDeprecated()) buf->writestring("deprecated "); emitProtection(buf, protection); buf->writestring("alias "); buf->writestring(toChars()); buf->writestring(";\n"); } } void TypedefDeclaration::toDocBuffer(OutBuffer *buf) { if (ident) { if (isDeprecated()) buf->writestring("deprecated "); emitProtection(buf, protection); buf->writestring("typedef "); buf->writestring(toChars()); buf->writestring(";\n"); } } void FuncDeclaration::toDocBuffer(OutBuffer *buf) { //printf("FuncDeclaration::toDocbuffer() %s\n", toChars()); if (ident) { TemplateDeclaration *td; if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) { /* It's a function template */ HdrGenState hgs; unsigned o = buf->offset; TypeFunction *tf = (TypeFunction *)type; hgs.ddoc = 1; prefix(buf, td); tf->next->toCBuffer(buf, NULL, &hgs); buf->writeByte(' '); buf->writestring(ident->toChars()); buf->writeByte('('); for (int i = 0; i < td->origParameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)td->origParameters->data[i]; if (i) buf->writestring(", "); tp->toCBuffer(buf, &hgs); } buf->writeByte(')'); Argument::argsToCBuffer(buf, &hgs, tf->parameters, tf->varargs); buf->writestring(";\n"); highlightCode(NULL, this, buf, o); } else { Declaration::toDocBuffer(buf); } } } void CtorDeclaration::toDocBuffer(OutBuffer *buf) { HdrGenState hgs; buf->writestring("this"); Argument::argsToCBuffer(buf, &hgs, arguments, varargs); buf->writestring(";\n"); } void AggregateDeclaration::toDocBuffer(OutBuffer *buf) { if (ident) { #if 0 emitProtection(buf, protection); #endif buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); buf->writestring(";\n"); } } void StructDeclaration::toDocBuffer(OutBuffer *buf) { //printf("StructDeclaration::toDocbuffer() %s\n", toChars()); if (ident) { #if 0 emitProtection(buf, protection); #endif TemplateDeclaration *td; if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) { unsigned o = buf->offset; td->toDocBuffer(buf); highlightCode(NULL, this, buf, o); } else { buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); } buf->writestring(";\n"); } } void ClassDeclaration::toDocBuffer(OutBuffer *buf) { //printf("ClassDeclaration::toDocbuffer() %s\n", toChars()); if (ident) { #if 0 emitProtection(buf, protection); #endif TemplateDeclaration *td; if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) { unsigned o = buf->offset; td->toDocBuffer(buf); highlightCode(NULL, this, buf, o); } else { if (isAbstract()) buf->writestring("abstract "); buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); } int any = 0; for (int i = 0; i < baseclasses.dim; i++) { BaseClass *bc = (BaseClass *)baseclasses.data[i]; if (bc->protection == PROTprivate) continue; if (bc->base && bc->base->ident == Id::Object) continue; if (any) buf->writestring(", "); else { buf->writestring(": "); any = 1; } emitProtection(buf, bc->protection); if (bc->base) { buf->writestring(bc->base->toPrettyChars()); } else { HdrGenState hgs; bc->type->toCBuffer(buf, NULL, &hgs); } } buf->writestring(";\n"); } } void EnumDeclaration::toDocBuffer(OutBuffer *buf) { if (ident) { buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); buf->writestring(";\n"); } } void EnumMember::toDocBuffer(OutBuffer *buf) { if (ident) { buf->writestring(toChars()); } } /********************************* DocComment *********************************/ DocComment::DocComment() { memset(this, 0, sizeof(DocComment)); } DocComment *DocComment::parse(Scope *sc, Dsymbol *s, unsigned char *comment) { unsigned idlen; //printf("parse(%s): '%s'\n", s->toChars(), comment); if (sc->lastdc && isDitto(comment)) return NULL; DocComment *dc = new DocComment(); if (!comment) return dc; dc->parseSections(comment); for (int i = 0; i < dc->sections.dim; i++) { Section *s = (Section *)dc->sections.data[i]; if (icmp("copyright", s->name, s->namelen) == 0) { dc->copyright = s; } if (icmp("macros", s->name, s->namelen) == 0) { dc->macros = s; } } sc->lastdc = dc; return dc; } /***************************************** * Parse next paragraph out of *pcomment. * Update *pcomment to point past paragraph. * Returns NULL if no more paragraphs. * If paragraph ends in 'identifier:', * then (*pcomment)[0 .. idlen] is the identifier. */ void DocComment::parseSections(unsigned char *comment) { unsigned char *p; unsigned char *pstart; unsigned char *pend; unsigned char *q; unsigned char *idstart; unsigned idlen; unsigned char *name = NULL; unsigned namelen = 0; //printf("parseSections('%s')\n", comment); p = comment; while (*p) { p = skipwhitespace(p); pstart = p; /* Find end of section, which is ended by one of: * 'identifier:' * '\0' */ idlen = 0; while (1) { if (isalpha(*p) || *p == '_') { q = p + 1; while (isalnum(*q) || *q == '_') q++; if (*q == ':') // identifier: ends it { idlen = q - p; idstart = p; for (pend = p; pend > pstart; pend--) { if (pend[-1] == '\n') break; } p = q + 1; break; } } while (1) { if (!*p) { pend = p; goto L1; } if (*p == '\n') { p++; if (*p == '\n' && !summary && !namelen) { pend = p; p++; goto L1; } break; } p++; } p = skipwhitespace(p); } L1: if (namelen || pstart < pend) { Section *s; if (icmp("Params", name, namelen) == 0) s = new ParamSection(); else if (icmp("Macros", name, namelen) == 0) s = new MacroSection(); else s = new Section(); s->name = name; s->namelen = namelen; s->body = pstart; s->bodylen = pend - pstart; s->nooutput = 0; //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body); sections.push(s); if (!summary && !namelen) summary = s; } if (idlen) { name = idstart; namelen = idlen; } else { name = NULL; namelen = 0; if (!*p) break; } } } void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) { //printf("DocComment::writeSections()\n"); if (sections.dim) { buf->writestring("$(DDOC_SECTIONS \n"); for (int i = 0; i < sections.dim; i++) { Section *sec = (Section *)sections.data[i]; if (sec->nooutput) continue; //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body); if (sec->namelen || i) sec->write(this, sc, s, buf); else { buf->writestring("$(DDOC_SUMMARY "); unsigned o = buf->offset; buf->write(sec->body, sec->bodylen); highlightText(sc, s, buf, o); buf->writestring(")\n"); } } buf->writestring(")\n"); } else { buf->writestring("$(DDOC_BLANKLINE)\n"); } } /*************************************************** */ void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) { if (namelen) { static const char *table[] = { "AUTHORS", "BUGS", "COPYRIGHT", "DATE", "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE", "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS", "VERSION" }; for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) { if (icmp(table[i], name, namelen) == 0) { buf->printf("$(DDOC_%s ", table[i]); goto L1; } } buf->writestring("$(DDOC_SECTION "); // Replace _ characters with spaces buf->writestring("$(DDOC_SECTION_H "); for (unsigned u = 0; u < namelen; u++) { unsigned char c = name[u]; buf->writeByte((c == '_') ? ' ' : c); } buf->writestring(":)\n"); } else { buf->writestring("$(DDOC_DESCRIPTION "); } L1: unsigned o = buf->offset; buf->write(body, bodylen); highlightText(sc, s, buf, o); buf->writestring(")\n"); } /*************************************************** */ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) { unsigned char *p = body; unsigned len = bodylen; unsigned char *pend = p + len; unsigned char *tempstart; unsigned templen; unsigned char *namestart; unsigned namelen = 0; // !=0 if line continuation unsigned char *textstart; unsigned textlen; unsigned o; Argument *arg; buf->writestring("$(DDOC_PARAMS \n"); while (p < pend) { // Skip to start of macro for (; 1; p++) { switch (*p) { case ' ': case '\t': continue; case '\n': p++; goto Lcont; default: if (!(isalpha(*p) || *p == '_')) { if (namelen) goto Ltext; // continuation of prev macro goto Lskipline; } break; } break; } tempstart = p; while (isalnum(*p) || *p == '_') p++; templen = p - tempstart; while (*p == ' ' || *p == '\t') p++; if (*p != '=') { if (namelen) goto Ltext; // continuation of prev macro goto Lskipline; } p++; if (namelen) { // Output existing param L1: //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); HdrGenState hgs; buf->writestring("$(DDOC_PARAM_ROW "); buf->writestring("$(DDOC_PARAM_ID "); o = buf->offset; arg = isFunctionParameter(s, namestart, namelen); if (arg && arg->type && arg->ident) arg->type->toCBuffer(buf, arg->ident, &hgs); else buf->write(namestart, namelen); highlightCode(sc, s, buf, o); buf->writestring(")\n"); buf->writestring("$(DDOC_PARAM_DESC "); o = buf->offset; buf->write(textstart, textlen); highlightText(sc, s, buf, o); buf->writestring(")"); buf->writestring(")\n"); namelen = 0; if (p >= pend) break; } namestart = tempstart; namelen = templen; while (*p == ' ' || *p == '\t') p++; textstart = p; Ltext: while (*p != '\n') p++; textlen = p - textstart; p++; Lcont: continue; Lskipline: // Ignore this line while (*p++ != '\n') ; } if (namelen) goto L1; // write out last one buf->writestring(")\n"); } /*************************************************** */ void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) { //printf("MacroSection::write()\n"); DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen); } /************************************************ * Parse macros out of Macros: section. * Macros are of the form: * name1 = value1 * * name2 = value2 */ void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen) { unsigned char *p = m; unsigned len = mlen; unsigned char *pend = p + len; unsigned char *tempstart; unsigned templen; unsigned char *namestart; unsigned namelen = 0; // !=0 if line continuation unsigned char *textstart; unsigned textlen; while (p < pend) { // Skip to start of macro for (; 1; p++) { if (p >= pend) goto Ldone; switch (*p) { case ' ': case '\t': continue; case '\n': p++; goto Lcont; default: if (!(isalpha(*p) || *p == '_')) { if (namelen) goto Ltext; // continuation of prev macro goto Lskipline; } break; } break; } tempstart = p; while (1) { if (p >= pend) goto Ldone; if (!(isalnum(*p) || *p == '_')) break; p++; } templen = p - tempstart; while (1) { if (p >= pend) goto Ldone; if (!(*p == ' ' || *p == '\t')) break; p++; } if (*p != '=') { if (namelen) goto Ltext; // continuation of prev macro goto Lskipline; } p++; if (p >= pend) goto Ldone; if (namelen) { // Output existing macro L1: //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); if (icmp("ESCAPES", namestart, namelen) == 0) parseEscapes(pescapetable, textstart, textlen); else Macro::define(pmacrotable, namestart, namelen, textstart, textlen); namelen = 0; if (p >= pend) break; } namestart = tempstart; namelen = templen; while (p < pend && (*p == ' ' || *p == '\t')) p++; textstart = p; Ltext: while (p < pend && *p != '\n') p++; textlen = p - textstart; // Remove trailing \r if there is one if (p > m && p[-1] == '\r') textlen--; p++; //printf("p = %p, pend = %p\n", p, pend); Lcont: continue; Lskipline: // Ignore this line while (p < pend && *p++ != '\n') ; } Ldone: if (namelen) goto L1; // write out last one } /************************************** * Parse escapes of the form: * /c/string/ * where c is a single character. * Multiple escapes can be separated * by whitespace and/or commas. */ void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen) { Escape *escapetable = *pescapetable; if (!escapetable) { escapetable = new Escape; *pescapetable = escapetable; } unsigned char *p = textstart; unsigned char *pend = p + textlen; while (1) { while (1) { if (p + 4 >= pend) return; if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ',')) break; p++; } if (p[0] != '/' || p[2] != '/') return; unsigned char c = p[1]; p += 3; unsigned char *start = p; while (1) { if (p >= pend) return; if (*p == '/') break; p++; } size_t len = p - start; char *s = (char *)memcpy(mem.malloc(len + 1), start, len); s[len] = 0; escapetable->strings[c] = s; //printf("%c = '%s'\n", c, s); p++; } } /****************************************** * Compare 0-terminated string with length terminated string. * Return < 0, ==0, > 0 */ int cmp(const char *stringz, void *s, size_t slen) { size_t len1 = strlen(stringz); if (len1 != slen) return len1 - slen; return memcmp(stringz, s, slen); } int icmp(const char *stringz, void *s, size_t slen) { size_t len1 = strlen(stringz); if (len1 != slen) return len1 - slen; return memicmp(stringz, (char *)s, slen); } /***************************************** * Return !=0 if comment consists entirely of "ditto". */ int isDitto(unsigned char *comment) { if (comment) { unsigned char *p = skipwhitespace(comment); if (memicmp((char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0) return 1; } return 0; } /********************************************** * Skip white space. */ unsigned char *skipwhitespace(unsigned char *p) { for (; 1; p++) { switch (*p) { case ' ': case '\t': case '\n': continue; } break; } return p; } /************************************************ * Scan forward to one of: * start of identifier * beginning of next line * end of buf */ unsigned skiptoident(OutBuffer *buf, unsigned i) { for (; i < buf->offset; i++) { // BUG: handle unicode alpha's unsigned char c = buf->data[i]; if (isalpha(c) || c == '_') break; if (c == '\n') break; } return i; } /************************************************ * Scan forward past end of identifier. */ unsigned skippastident(OutBuffer *buf, unsigned i) { for (; i < buf->offset; i++) { // BUG: handle unicode alpha's unsigned char c = buf->data[i]; if (!(isalnum(c) || c == '_')) break; } return i; } /************************************************ * Scan forward past URL starting at i. * We don't want to highlight parts of a URL. * Returns: * i if not a URL * index just past it if it is a URL */ unsigned skippastURL(OutBuffer *buf, unsigned i) { unsigned length = buf->offset - i; unsigned char *p = &buf->data[i]; unsigned j; unsigned sawdot = 0; if (length > 7 && memicmp((char *)p, "http://", 7) == 0) { j = 7; } else if (length > 8 && memicmp((char *)p, "https://", 8) == 0) { j = 8; } else goto Lno; for (; j < length; j++) { unsigned char c = p[j]; if (isalnum(c)) continue; if (c == '-' || c == '_' || c == '?' || c == '=' || c == '%' || c == '&' || c == '/' || c == '+' || c == '#' || c == '~') continue; if (c == '.') { sawdot = 1; continue; } break; } if (sawdot) return i + j; Lno: return i; } /**************************************************** */ int isKeyword(unsigned char *p, unsigned len) { static const char *table[] = { "true", "false", "null" }; for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) { if (cmp(table[i], p, len) == 0) return 1; } return 0; } /**************************************************** */ Argument *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len) { FuncDeclaration *f = s->isFuncDeclaration(); /* f->type may be NULL for template members. */ if (f && f->type) { TypeFunction *tf; if (f->originalType) { tf = (TypeFunction *)f->originalType; } else tf = (TypeFunction *)f->type; if (tf->parameters) { for (size_t k = 0; k < tf->parameters->dim; k++) { Argument *arg = (Argument *)tf->parameters->data[k]; if (arg->ident && cmp(arg->ident->toChars(), p, len) == 0) { return arg; } } } } return NULL; } /************************************************** * Highlight text section. */ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) { //printf("highlightText()\n"); const char *sid = s->ident->toChars(); FuncDeclaration *f = s->isFuncDeclaration(); unsigned char *p; const char *se; int leadingBlank = 1; int inCode = 0; int inComment = 0; // in <!-- ... --> comment unsigned iCodeStart; // start of code section unsigned iLineStart = offset; for (unsigned i = offset; i < buf->offset; i++) { unsigned char c = buf->data[i]; Lcont: switch (c) { case ' ': case '\t': break; case '\n': if (sc && !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n" { static char blankline[] = "$(DDOC_BLANKLINE)\n"; i = buf->insert(i, blankline, sizeof(blankline) - 1); } leadingBlank = 1; iLineStart = i + 1; break; case '<': leadingBlank = 0; if (inCode) break; p = &buf->data[i]; // Skip over comments if (p[1] == '!' && p[2] == '-' && p[3] == '-') { unsigned j = i + 4; p += 4; while (1) { if (j == buf->offset) goto L1; if (p[0] == '-' && p[1] == '-' && p[2] == '>') { i = j + 2; // place on closing '>' break; } j++; p++; } break; } // Skip over HTML tag if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2]))) { unsigned j = i + 2; p += 2; while (1) { if (j == buf->offset) goto L1; if (p[0] == '>') { i = j; // place on closing '>' break; } j++; p++; } break; } L1: // Replace '<' with '<' character entity se = Escape::escapeChar('<'); if (se) { size_t len = strlen(se); buf->remove(i, 1); i = buf->insert(i, se, len); i--; // point to ';' } break; case '>': leadingBlank = 0; if (inCode) break; // Replace '>' with '>' character entity se = Escape::escapeChar('>'); if (se) { size_t len = strlen(se); buf->remove(i, 1); i = buf->insert(i, se, len); i--; // point to ';' } break; case '&': leadingBlank = 0; if (inCode) break; p = &buf->data[i]; if (p[1] == '#' || isalpha(p[1])) break; // already a character entity // Replace '&' with '&' character entity se = Escape::escapeChar('&'); if (se) { size_t len = strlen(se); buf->remove(i, 1); i = buf->insert(i, se, len); i--; // point to ';' } break; case '-': /* A line beginning with --- delimits a code section. * inCode tells us if it is start or end of a code section. */ if (leadingBlank) { int istart = i; int eollen = 0; leadingBlank = 0; while (1) { ++i; if (i >= buf->offset) break; c = buf->data[i]; if (c == '\n') { eollen = 1; break; } if (c == '\r') { eollen = 1; if (i + 1 >= buf->offset) break; if (buf->data[i + 1] == '\n') { eollen = 2; break; } } // BUG: handle UTF PS and LS too if (c != '-') goto Lcont; } if (i - istart < 3) goto Lcont; // We have the start/end of a code section // Remove the entire --- line, including blanks and \n buf->remove(iLineStart, i - iLineStart + eollen); i = iLineStart; if (inCode) { inCode = 0; // The code section is from iCodeStart to i OutBuffer codebuf; codebuf.write(buf->data + iCodeStart, i - iCodeStart); codebuf.writeByte(0); highlightCode2(sc, s, &codebuf, 0); buf->remove(iCodeStart, i - iCodeStart); i = buf->insert(iCodeStart, codebuf.data, codebuf.offset); i = buf->insert(i, ")\n", 2); i--; } else { static char pre[] = "$(D_CODE \n"; inCode = 1; i = buf->insert(i, pre, sizeof(pre) - 1); iCodeStart = i; i--; // place i on > } } break; default: leadingBlank = 0; if (sc && !inCode && (isalpha(c) || c == '_')) { unsigned j; j = skippastident(buf, i); if (j > i) { unsigned k = skippastURL(buf, i); if (k > i) { i = k - 1; break; } if (buf->data[i] == '_') // leading '_' means no highlight { buf->remove(i, 1); i = j - 1; } else { if (cmp(sid, buf->data + i, j - i) == 0) { i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; break; } else if (isKeyword(buf->data + i, j - i)) { i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1; break; } else { if (f && isFunctionParameter(f, buf->data + i, j - i)) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; break; } } i = j - 1; } } } break; } } Ldone: ; } /************************************************** * Highlight code for DDOC section. */ void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) { char *sid = s->ident->toChars(); FuncDeclaration *f = s->isFuncDeclaration(); //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind()); for (unsigned i = offset; i < buf->offset; i++) { unsigned char c = buf->data[i]; const char *se; se = Escape::escapeChar(c); if (se) { size_t len = strlen(se); buf->remove(i, 1); i = buf->insert(i, se, len); i--; // point to ';' } else if (isalpha(c) || c == '_') { unsigned j; j = skippastident(buf, i); if (j > i) { if (cmp(sid, buf->data + i, j - i) == 0) { i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; continue; } else if (f) { if (isFunctionParameter(f, buf->data + i, j - i)) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; continue; } } i = j - 1; } } } } /**************************************** */ void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend) { for (; p < pend; p++) { const char *s = Escape::escapeChar(*p); if (s) buf->writestring(s); else buf->writeByte(*p); } } /************************************************** * Highlight code for CODE section. */ void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) { char *sid = s->ident->toChars(); FuncDeclaration *f = s->isFuncDeclaration(); unsigned errorsave = global.errors; Lexer lex(NULL, buf->data, 0, buf->offset - 1, 0, 1); Token tok; OutBuffer res; unsigned char *lastp = buf->data; const char *highlight; //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data); res.reserve(buf->offset); while (1) { lex.scan(&tok); highlightCode3(&res, lastp, tok.ptr); highlight = NULL; switch (tok.value) { case TOKidentifier: if (!sc) break; if (cmp(sid, tok.ptr, lex.p - tok.ptr) == 0) { highlight = "$(D_PSYMBOL "; break; } else if (f) { if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr)) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); highlight = "$(D_PARAM "; break; } } break; case TOKcomment: highlight = "$(D_COMMENT "; break; case TOKstring: highlight = "$(D_STRING "; break; default: if (tok.isKeyword()) highlight = "$(D_KEYWORD "; break; } if (highlight) res.writestring(highlight); highlightCode3(&res, tok.ptr, lex.p); if (highlight) res.writeByte(')'); if (tok.value == TOKeof) break; lastp = lex.p; } buf->setsize(offset); buf->write(&res); global.errors = errorsave; } /*************************************** * Find character string to replace c with. */ const char *Escape::escapeChar(unsigned c) { const char *s; switch (c) { case '<': s = "<"; break; case '>': s = ">"; break; case '&': s = "&"; break; default: s = NULL; break; } return s; }