Mercurial > projects > ldc
view dmd2/doc.c @ 1317:4099548c80e0
Allocate objects on the stack if they (a) don't have a destructor, and
(b) don't override the delete operator (on top of the regular conditions for
stack allocation that also apply to arrays, structs, etc.).
The "no destructor" clause is not strictly necessary, but calling them at the
right time would be tricky to say the least; it would involve, among other
things, "manually" inserting a try-finally block around anything that might
throw exceptions not caught in the current function.
Note: objects with custom new operators are automatically ignored because they
don't use the regular allocation runtime call, so there's no need to pay special
attention to them.
author | Frits van Bommel <fvbommel wxs.nl> |
---|---|
date | Sat, 09 May 2009 00:50:15 +0200 |
parents | f04dde6e882c |
children | 638d16625da2 |
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> #if IN_GCC || IN_LLVM #include "mem.h" #else #if _WIN32 #include "..\root\mem.h" #elif POSIX #include "../root/mem.h" #else #error "fix this" #endif #endif #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/2.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 { 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; }