// // // The Nimrod Compiler // (c) Copyright 2008 Andreas Rumpf // // See the file "copying.txt", included in this // distribution, for details about the copyright. // unit docgen; // This is the documentation generator. It is currently pretty simple: No // semantic checking is done for the code. Cross-references are generated // by knowing how the anchors are going to be named. interface {$include 'config.inc'} uses nsystem, charsets, ast, astalgo, strutils, nhashes, options, nversion, msgs, nos, ropes, idents, wordrecg, nmath, pnimsyn, rnimsyn, scanner, rst, ntime, highlite; procedure CommandDoc(const filename: string); procedure CommandRst2Html(const filename: string); implementation type TTocEntry = record n: PRstNode; refname, header: PRope; end; TSections = array [TSymKind] of PRope; TMetaEnum = (metaNone, metaTitle, metaSubtitle); TDocumentor = record // contains a module's documentation filename: string; // filename of the source file; without extension basedir: string; // base directory (where to put the documentation) modDesc: PRope; // module description dependsOn: PRope; // dependencies id: int; // for generating IDs splitAfter: int; // split too long entries in the TOC tocPart: array of TTocEntry; hasToc: bool; toc, section: TSections; indexFile, theIndex: PRstNode; indexValFilename: string; indent, verbatim: int; // for code generation meta: array [TMetaEnum] of PRope; end; PDoc = ^TDocumentor; function findIndexNode(n: PRstNode): PRstNode; var i: int; begin if n = nil then result := nil else if n.kind = rnIndex then begin result := n.sons[2]; if result = nil then begin result := newRstNode(rnDefList); n.sons[2] := result end else if result.kind = rnInner then result := result.sons[0] end else begin result := nil; for i := 0 to rsonsLen(n)-1 do begin result := findIndexNode(n.sons[i]); if result <> nil then exit end end end; procedure initIndexFile(d: PDoc); var h: PRstNode; dummyHasToc: bool; begin if gIndexFile = '' then exit; gIndexFile := appendFileExt(gIndexFile, 'txt'); d.indexValFilename := changeFileExt(extractFilename(d.filename), HtmlExt); if ExistsFile(gIndexFile) then begin d.indexFile := rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1, dummyHasToc); d.theIndex := findIndexNode(d.indexFile); if (d.theIndex = nil) or (d.theIndex.kind <> rnDefList) then rawMessage(errXisNoValidIndexFile, gIndexFile); clearIndex(d.theIndex, d.indexValFilename); end else begin d.indexFile := newRstNode(rnInner); h := newRstNode(rnOverline); h.level := 1; addSon(h, newRstNode(rnLeaf, 'Index')); addSon(d.indexFile, h); h := newRstNode(rnIndex); addSon(h, nil); // no argument addSon(h, nil); // no options d.theIndex := newRstNode(rnDefList); addSon(h, d.theIndex); addSon(d.indexFile, h); end end; function newDocumentor(const filename: string): PDoc; var s: string; begin new(result); {@ignore} fillChar(result^, sizeof(result^), 0); {@emit result.tocPart := @[]; } result.filename := filename; result.id := 100; result.splitAfter := 20; s := getConfigVar('split.item.toc'); if s <> '' then result.splitAfter := parseInt(s); end; function getVarIdx(const varnames: array of string; const id: string): int; var i: int; begin for i := 0 to high(varnames) do if cmpIgnoreStyle(varnames[i], id) = 0 then begin result := i; exit end; result := -1 end; function ropeFormatNamedVars(const frmt: TFormatStr; const varnames: array of string; const varvalues: array of PRope): PRope; var i, j, L, start, idx: int; id: string; begin i := strStart; L := length(frmt); result := nil; while i <= L + StrStart - 1 do begin if frmt[i] = '$' then begin inc(i); // skip '$' case frmt[i] of '$': begin app(result, '$'+''); inc(i) end; '0'..'9': begin j := 0; while true do begin j := (j * 10) + Ord(frmt[i]) - ord('0'); inc(i); if (i > L+StrStart-1) or not (frmt[i] in ['0'..'9']) then break end; if j > high(varvalues) + 1 then internalError('ropeFormatNamedVars'); app(result, varvalues[j - 1]) end; 'A'..'Z', 'a'..'z', #128..#255: begin id := ''; while true do begin addChar(id, frmt[i]); inc(i); if not (frmt[i] in ['A'..'Z', '_', 'a'..'z', #128..#255]) then break end; // search for the variable: idx := getVarIdx(varnames, id); if idx >= 0 then app(result, varvalues[idx]) else rawMessage(errUnkownSubstitionVar, id) end; '{': begin id := ''; inc(i); while frmt[i] <> '}' do begin if frmt[i] = #0 then rawMessage(errTokenExpected, '}'+''); addChar(id, frmt[i]); inc(i); end; inc(i); // skip } // search for the variable: idx := getVarIdx(varnames, id); if idx >= 0 then app(result, varvalues[idx]) else rawMessage(errUnkownSubstitionVar, id) end else InternalError('ropeFormatNamedVars') end end; start := i; while (i <= L + StrStart - 1) do begin if (frmt[i] <> '$') then inc(i) else break end; if i - 1 >= start then app(result, ncopy(frmt, start, i - 1)) end end; procedure addXmlChar(var dest: string; c: Char); begin case c of '&': add(dest, '&'); '<': add(dest, '<'); '>': add(dest, '>'); '"': add(dest, '"'); else addChar(dest, c) end end; function nextSplitPoint(const s: string; start: int): int; begin result := start; while result < length(s)+strStart do begin case s[result] of '_': exit; 'a'..'z': begin if result+1 < length(s)+strStart then if s[result+1] in ['A'..'Z'] then exit; end; else begin end; end; inc(result); end; dec(result); // last valid index end; function toXml(const s: string; splitAfter: int = -1): string; const splitter = ''; var i, j, k, partLen: int; begin result := ''; if splitAfter >= 0 then begin partLen := 0; j := strStart; while j < length(s)+strStart do begin k := nextSplitPoint(s, j); if (splitter <> ' '+'') or (partLen + k - j + 1 > splitAfter) then begin partLen := 0; add(result, splitter); end; for i := j to k do addXmlChar(result, s[i]); inc(partLen, k - j + 1); j := k+1; end; end else begin for i := strStart to length(s)+strStart-1 do addXmlChar(result, s[i]) end end; function renderRstToHtml(d: PDoc; n: PRstNode): PRope; forward; function renderAux(d: PDoc; n: PRstNode; const outer: string = '$1'; const inner: string = '$1'): PRope; var i: int; begin result := nil; for i := 0 to rsonsLen(n)-1 do appf(result, inner, [renderRstToHtml(d, n.sons[i])]); result := ropef(outer, [result]); end; procedure setIndexForSourceTerm(d: PDoc; name: PRstNode; id: int); var a, h: PRstNode; begin if d.theIndex = nil then exit; h := newRstNode(rnHyperlink); a := newRstNode(rnLeaf, d.indexValFilename +{&} '#' +{&} toString(id)); addSon(h, a); addSon(h, a); a := newRstNode(rnIdx); addSon(a, name); setIndexPair(d.theIndex, a, h); end; function renderIndexTerm(d: PDoc; n: PRstNode): PRope; var a, h: PRstNode; begin inc(d.id); result := ropef('$2', [toRope(d.id), renderAux(d, n)]); h := newRstNode(rnHyperlink); a := newRstNode(rnLeaf, d.indexValFilename +{&} '#' +{&} toString(d.id)); addSon(h, a); addSon(h, a); setIndexPair(d.theIndex, n, h); end; function genComment(d: PDoc; n: PNode): PRope; var dummyHasToc: bool; begin if (n.comment <> snil) and startsWith(n.comment, '##') then result := renderRstToHtml(d, rstParse(n.comment, true, toFilename(n.info), toLineNumber(n.info), toColumn(n.info), dummyHasToc)) else result := nil; end; function genRecComment(d: PDoc; n: PNode): PRope; var i: int; begin if n = nil then begin result := nil; exit end; result := genComment(d, n); if result = nil then begin if not (n.kind in [nkEmpty..nkNilLit]) then for i := 0 to sonsLen(n)-1 do begin result := genRecComment(d, n.sons[i]); if result <> nil then exit end end else n.comment := snil end; function isVisible(n: PNode): bool; var v: PIdent; begin result := false; if n.kind = nkPostfix then begin if (sonsLen(n) = 2) and (n.sons[0].kind = nkIdent) then begin v := n.sons[0].ident; result := (v.id = ord(wStar)) or (v.id = ord(wMinus)); end end else if n.kind = nkSym then result := sfInInterface in n.sym.flags else if n.kind = nkPragmaExpr then result := isVisible(n.sons[0]); end; function getName(n: PNode; splitAfter: int = -1): string; begin case n.kind of nkPostfix: result := getName(n.sons[1], splitAfter); nkPragmaExpr: result := getName(n.sons[0], splitAfter); nkSym: result := toXML(n.sym.name.s, splitAfter); nkIdent: result := toXML(n.ident.s, splitAfter); nkAccQuoted: result := '`' +{&} getName(n.sons[0], splitAfter) +{&} '`'; else begin internalError(n.info, 'getName()'); result := '' end end end; function getRstName(n: PNode): PRstNode; begin case n.kind of nkPostfix: result := getRstName(n.sons[1]); nkPragmaExpr: result := getRstName(n.sons[0]); nkSym: result := newRstNode(rnLeaf, n.sym.name.s); nkIdent: result := newRstNode(rnLeaf, n.ident.s); nkAccQuoted: result := getRstName(n.sons[0]); else begin internalError(n.info, 'getRstName()'); result := nil end end end; procedure genItem(d: PDoc; n, nameNode: PNode; k: TSymKind); var r: TSrcGen; kind: TTokType; literal: string; name, result, comm: PRope; begin if not isVisible(nameNode) then exit; name := toRope(getName(nameNode)); result := nil; literal := ''; kind := tkEof; {@ignore} fillChar(r, sizeof(r), 0); {@emit} comm := genRecComment(d, n); // call this here for the side-effect! initTokRender(r, n, {@set}[renderNoPragmas, renderNoBody, renderNoComments, renderDocComments]); while true do begin getNextTok(r, kind, literal); case kind of tkEof: break; tkComment: appf(result, '$1', [toRope(toXml(literal))]); tokKeywordLow..tokKeywordHigh: appf(result, '$1', [toRope(literal)]); tkOpr, tkHat: appf(result, '$1', [toRope(toXml(literal))]); tkStrLit..tkTripleStrLit: appf(result, '$1', [toRope(toXml(literal))]); tkCharLit: appf(result, '$1', [toRope(toXml(literal))]); tkIntLit..tkInt64Lit: appf(result, '$1', [toRope(literal)]); tkFloatLit..tkFloat64Lit: appf(result, '$1', [toRope(literal)]); tkSymbol: appf(result, '$1', [toRope(literal)]); tkInd, tkSad, tkDed, tkSpaces: app(result, literal); //appf(result, '$1', // [toRope(literal)]); tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkAccent: appf(result, '$1', [toRope(literal)]); else InternalError(n.info, 'docgen.genThing(' + toktypeToStr[kind] + ')'); end end; inc(d.id); app(d.section[k], ropeFormatNamedVars(getConfigVar('doc.item'), ['name', 'header', 'desc', 'itemID'], [name, result, comm, toRope(d.id)])); app(d.toc[k], ropeFormatNamedVars(getConfigVar('doc.item.toc'), ['name', 'header', 'desc', 'itemID'], [toRope(getName(nameNode, d.splitAfter)), result, comm, toRope(d.id)])); setIndexForSourceTerm(d, getRstName(nameNode), d.id); end; function renderHeadline(d: PDoc; n: PRstNode): PRope; var i, len: int; refname: PRope; begin result := nil; for i := 0 to rsonsLen(n)-1 do app(result, renderRstToHtml(d, n.sons[i])); refname := toRope(rstnodeToRefname(n)); if d.hasToc then begin len := length(d.tocPart); setLength(d.tocPart, len+1); d.tocPart[len].refname := refname; d.tocPart[len].n := n; d.tocPart[len].header := result; result := ropef('$3'+ '', [toRope(n.level), d.tocPart[len].refname, result]); end else result := ropef('$3', [toRope(n.level), refname, result]); end; function renderOverline(d: PDoc; n: PRstNode): PRope; var i: int; t: PRope; begin t := nil; for i := 0 to rsonsLen(n)-1 do app(t, renderRstToHtml(d, n.sons[i])); result := nil; if d.meta[metaTitle] = nil then d.meta[metaTitle] := t else if d.meta[metaSubtitle] = nil then d.meta[metaSubtitle] := t else result := ropef('
$3
', [toRope(n.level), toRope(rstnodeToRefname(n)), t]); end; function renderRstToRst(d: PDoc; n: PRstNode): PRope; forward; function renderRstSons(d: PDoc; n: PRstNode): PRope; var i: int; begin result := nil; for i := 0 to rsonsLen(n)-1 do app(result, renderRstToRst(d, n.sons[i])); end; function renderRstToRst(d: PDoc; n: PRstNode): PRope; // this is needed for the index generation; it may also be useful for // debugging, but most code is already debugged... const lvlToChar: array [0..8] of char = ('!', '=', '-', '~', '`', '<', '*', '|', '+'); var L: int; ind: PRope; begin result := nil; if n = nil then exit; ind := toRope(repeatChar(d.indent)); case n.kind of rnInner: result := renderRstSons(d, n); rnHeadline: begin result := renderRstSons(d, n); L := ropeLen(result); result := ropef('$n$1$2$n$1$3', [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))]); end; rnOverline: begin result := renderRstSons(d, n); L := ropeLen(result); result := ropef('$n$1$3$n$1$2$n$1$3', [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))]); end; rnTransition: result := ropef('$n$n$1$2$n$n', [ind, toRope(repeatChar(78-d.indent, '-'))]); rnParagraph: begin result := renderRstSons(d, n); result := ropef('$n$n$1$2', [ind, result]); end; rnBulletItem: begin inc(d.indent, 2); result := renderRstSons(d, n); if result <> nil then result := ropef('$n$1* $2', [ind, result]); dec(d.indent, 2); end; rnEnumItem: begin inc(d.indent, 4); result := renderRstSons(d, n); if result <> nil then result := ropef('$n$1(#) $2', [ind, result]); dec(d.indent, 4); end; rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: result := renderRstSons(d, n); rnDefName: begin result := renderRstSons(d, n); result := ropef('$n$n$1$2', [ind, result]); end; rnDefBody: begin inc(d.indent, 2); result := renderRstSons(d, n); if n.sons[0].kind <> rnBulletList then result := ropef('$n$1 $2', [ind, result]); dec(d.indent, 2); end; rnField: begin result := renderRstToRst(d, n.sons[0]); L := max(ropeLen(result)+3, 30); inc(d.indent, L); result := ropef('$n$1:$2:$3$4', [ ind, result, toRope(repeatChar(L-ropeLen(result)-2)), renderRstToRst(d, n.sons[1])]); dec(d.indent, L); end; rnLineBlockItem: begin result := renderRstSons(d, n); result := ropef('$n$1| $2', [ind, result]); end; rnBlockQuote: begin inc(d.indent, 2); result := renderRstSons(d, n); dec(d.indent, 2); end; rnRef: begin result := renderRstSons(d, n); result := ropef('`$1`_', [result]); end; rnHyperlink: begin result := ropef('`$1 <$2>`_', [renderRstToRst(d, n.sons[0]), renderRstToRst(d, n.sons[1])]); end; rnGeneralRole: begin result := renderRstToRst(d, n.sons[0]); result := ropef('`$1`:$2:', [result, renderRstToRst(d, n.sons[1])]); end; rnSub: begin result := renderRstSons(d, n); result := ropef('`$1`:sub:', [result]); end; rnSup: begin result := renderRstSons(d, n); result := ropef('`$1`:sup:', [result]); end; rnIdx: begin result := renderRstSons(d, n); result := ropef('`$1`:idx:', [result]); end; rnEmphasis: begin result := renderRstSons(d, n); result := ropef('*$1*', [result]); end; rnStrongEmphasis: begin result := renderRstSons(d, n); result := ropef('**$1**', [result]); end; rnInterpretedText: begin result := renderRstSons(d, n); result := ropef('`$1`', [result]); end; rnInlineLiteral: begin inc(d.verbatim); result := renderRstSons(d, n); result := ropef('``$1``', [result]); dec(d.verbatim); end; rnLeaf: begin if (d.verbatim = 0) and (n.text = '\'+'') then result := toRope('\\') // XXX: escape more special characters! else result := toRope(n.text); end; rnIndex: begin inc(d.indent, 3); if n.sons[2] <> nil then result := renderRstSons(d, n.sons[2]); dec(d.indent, 3); result := ropef('$n$n$1.. index::$n$2', [ind, result]); end; rnContents: begin result := ropef('$n$n$1.. contents::', [ind]); end; else rawMessage(errCannotRenderX, rstnodeKindToStr[n.kind]); end; end; function renderTocEntry(d: PDoc; const e: TTocEntry): PRope; begin result := ropef('
  • $2' + '
  • $n', [e.refname, e.header]); end; function renderTocEntries(d: PDoc; var j: int; lvl: int): PRope; var a: int; begin result := nil; while (j <= high(d.tocPart)) do begin a := abs(d.tocPart[j].n.level); if (a = lvl) then begin app(result, renderTocEntry(d, d.tocPart[j])); inc(j); end else if (a > lvl) then app(result, renderTocEntries(d, j, a)) else break end; if lvl > 1 then result := ropef('', [result]); end; function fieldAux(const s: string): PRope; begin result := toRope(strip(s)) end; function renderImage(d: PDoc; n: PRstNode): PRope; var s: string; begin result := ropef(' '' then appf(result, ' height="$1"', [fieldAux(s)]); s := getFieldValue(n, 'width'); if s <> '' then appf(result, ' width="$1"', [fieldAux(s)]); s := getFieldValue(n, 'scale'); if s <> '' then appf(result, ' scale="$1"', [fieldAux(s)]); s := getFieldValue(n, 'alt'); if s <> '' then appf(result, ' alt="$1"', [fieldAux(s)]); s := getFieldValue(n, 'align'); if s <> '' then appf(result, ' align="$1"', [fieldAux(s)]); app(result, ' />'); if rsonsLen(n) >= 3 then app(result, renderRstToHtml(d, n.sons[2])) end; function renderCodeBlock(d: PDoc; n: PRstNode): PRope; var m: PRstNode; g: TGeneralTokenizer; langstr: string; lang: TSourceLanguage; begin result := nil; if n.sons[2] = nil then exit; m := n.sons[2].sons[0]; if (m.kind <> rnLeaf) then InternalError('renderCodeBlock'); langstr := strip(getArgument(n)); if langstr = '' then lang := langNimrod // default language else lang := getSourceLanguage(langstr); if lang = langNone then begin rawMessage(warnLanguageXNotSupported, langstr); result := ropef('
    $1
    ', [toRope(m.text)]) end else begin initGeneralTokenizer(g, m.text); while true do begin getNextToken(g, lang); case g.kind of gtEof: break; gtNone, gtWhitespace: app(result, ncopy(m.text, g.start+strStart, g.len+g.start-1+strStart)); else appf(result, '$1', [toRope(toXml(ncopy(m.text, g.start+strStart, g.len+g.start-1+strStart))), toRope(tokenClassToStr[g.kind])]); end; end; deinitGeneralTokenizer(g); if result <> nil then result := ropef('
    $1
    ', [result]); end end; function renderContainer(d: PDoc; n: PRstNode): PRope; var arg: PRope; begin result := renderRstToHtml(d, n.sons[2]); arg := toRope(strip(getArgument(n))); if arg = nil then result := ropef('
    $1
    ', [result]) else result := ropef('
    $2
    ', [arg, result]) end; function renderRstToHtml(d: PDoc; n: PRstNode): PRope; var outer, inner: string; begin if n = nil then begin result := nil; exit end; outer := '$1'; inner := '$1'; case n.kind of rnInner: begin end; rnHeadline: begin result := renderHeadline(d, n); exit; end; rnOverline: begin result := renderOverline(d, n); exit; end; rnTransition: outer := '
    '+nl; rnParagraph: outer := '

    $1

    '+nl; rnBulletList: outer := ''+nl; rnBulletItem, rnEnumItem: outer := '
  • $1
  • '+nl; rnEnumList: outer := '
      $1
    '+nl; rnDefList: outer := '
    $1
    '+nl; rnDefItem: begin end; rnDefName: outer := '
    $1
    '+nl; rnDefBody: outer := '
    $1
    '+nl; rnFieldList: outer := '' + '' + '' + '$1' + '
    '; rnField: outer := '$1$n'; rnFieldName: outer := '$1:'; rnFieldBody: outer := '$1'; rnIndex: begin result := renderRstToHtml(d, n.sons[2]); exit end; rnOptionList: outer := '$1
    '; rnOptionListItem: outer := '$1$n'; rnOptionGroup: outer := '$1'; rnDescription: outer := '$1$n'; rnOption, rnOptionString, rnOptionArgument: InternalError('renderRstToHtml'); rnLiteralBlock: outer := '
    $1
    '+nl; rnQuotedLiteralBlock: InternalError('renderRstToHtml'); rnLineBlock: outer := '

    $1

    '; rnLineBlockItem: outer := '$1
    '; rnBlockQuote: outer := '

    $1

    $n'; rnTable, rnGridTable: outer := '$1
    '; rnTableRow: outer := '$1$n'; rnTableDataCell: outer := '$1'; rnTableHeaderCell: outer := '$1'; rnLabel: InternalError('renderRstToHtml'); // used for footnotes and other rnFootnote: InternalError('renderRstToHtml'); // a footnote rnCitation: InternalError('renderRstToHtml'); // similar to footnote rnRef: begin result := ropef('$1', [renderAux(d, n), toRope(rstnodeToRefname(n))]); exit end; rnStandaloneHyperlink: outer := '$1'; rnHyperlink: begin result := ropef('$1', [renderRstToHtml(d, n.sons[0]), renderRstToHtml(d, n.sons[1])]); exit end; rnDirArg, rnRaw: begin end; rnImage, rnFigure: begin result := renderImage(d, n); exit end; rnCodeBlock: begin result := renderCodeBlock(d, n); exit end; rnContainer: begin result := renderContainer(d, n); exit end; rnSubstitutionReferences, rnSubstitutionDef: outer := '|$1|'; rnDirective: outer := ''; // Inline markup: rnGeneralRole: begin result := ropef('$1', [renderRstToHtml(d, n.sons[0]), renderRstToHtml(d, n.sons[1])]); exit end; rnSub: outer := '$1'; rnSup: outer := '$1'; rnEmphasis: outer := '$1'; rnStrongEmphasis: outer := '$1'; rnInterpretedText: outer := '$1'; rnIdx: begin if d.theIndex = nil then outer := '$1' else begin result := renderIndexTerm(d, n); exit end end; rnInlineLiteral: outer := '' +{&} '$1'; rnLeaf: begin result := toRope(toXml(n.text)); exit end; rnContents: begin d.hasToc := true; exit; end; rnTitle: begin d.meta[metaTitle] := renderRstToHtml(d, n.sons[0]); exit end; else InternalError('renderRstToHtml'); end; result := renderAux(d, n, outer, inner); end; procedure generateDoc(d: PDoc; n: PNode); var i: int; begin if n = nil then exit; case n.kind of nkCommentStmt: app(d.modDesc, genComment(d, n)); nkProcDef: genItem(d, n, n.sons[namePos], skProc); nkIteratorDef: genItem(d, n, n.sons[namePos], skIterator); nkMacroDef: genItem(d, n, n.sons[namePos], skMacro); nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate); nkConverterDef: genItem(d, n, n.sons[namePos], skConverter); nkVarSection: begin for i := 0 to sonsLen(n)-1 do if n.sons[i].kind <> nkCommentStmt then genItem(d, n.sons[i], n.sons[i].sons[0], skVar); end; nkConstSection: begin for i := 0 to sonsLen(n)-1 do if n.sons[i].kind <> nkCommentStmt then genItem(d, n.sons[i], n.sons[i].sons[0], skConst); end; nkTypeSection: begin for i := 0 to sonsLen(n)-1 do if n.sons[i].kind <> nkCommentStmt then genItem(d, n.sons[i], n.sons[i].sons[0], skType); end; nkStmtList: begin for i := 0 to sonsLen(n)-1 do generateDoc(d, n.sons[i]); end; nkWhenStmt: begin // generate documentation for the first branch only: generateDoc(d, lastSon(n.sons[0])); end else begin end end end; procedure genSection(d: PDoc; kind: TSymKind); var title: PRope; begin if d.section[kind] = nil then exit; title := toRope(ncopy(symKindToStr[kind], strStart+2) + 's'); d.section[kind] := ropeFormatNamedVars(getConfigVar('doc.section'), ['sectionid', 'sectionTitle', 'sectionTitleID', 'content'], [toRope(ord(kind)), title, toRope(ord(kind)+50), d.section[kind]]); d.toc[kind] := ropeFormatNamedVars(getConfigVar('doc.section.toc'), ['sectionid', 'sectionTitle', 'sectionTitleID', 'content'], [toRope(ord(kind)), title, toRope(ord(kind)+50), d.toc[kind]]); end; function genHtmlFile(d: PDoc): PRope; var code, toc, title, content: PRope; bodyname: string; i: TSymKind; j: int; begin j := 0; toc := renderTocEntries(d, j, 1); code := nil; content := nil; title := nil; for i := low(TSymKind) to high(TSymKind) do begin genSection(d, i); app(toc, d.toc[i]); end; if toc <> nil then toc := ropeFormatNamedVars(getConfigVar('doc.toc'), ['content'], [toc]); for i := low(TSymKind) to high(TSymKind) do app(code, d.section[i]); if d.meta[metaTitle] <> nil then title := d.meta[metaTitle] else title := toRope('Module ' + extractFilename(changeFileExt(d.filename, ''))); if d.hasToc then bodyname := 'doc.body_toc' else bodyname := 'doc.body_no_toc'; content := ropeFormatNamedVars(getConfigVar(bodyname), ['title', 'tableofcontents', 'moduledesc', 'date', 'time', 'content'], [title, toc, d.modDesc, toRope(getDateStr()), toRope(getClockStr()), code]); if not (optCompileOnly in gGlobalOptions) then code := ropeFormatNamedVars(getConfigVar('doc.file'), ['title', 'tableofcontents', 'moduledesc', 'date', 'time', 'content'], [title, toc, d.modDesc, toRope(getDateStr()), toRope(getClockStr()), content]) else code := content; result := code; end; procedure generateIndex(d: PDoc); begin if d.theIndex <> nil then begin sortIndex(d.theIndex); writeRope(renderRstToRst(d, d.indexFile), gIndexFile); end end; procedure CommandDoc(const filename: string); var ast: PNode; d: PDoc; begin ast := parseFile(appendFileExt(filename, nimExt)); if ast = nil then exit; d := newDocumentor(filename); initIndexFile(d); d.hasToc := true; generateDoc(d, ast); writeRope(genHtmlFile(d), getOutFile(filename, HtmlExt)); generateIndex(d); end; procedure CommandRst2Html(const filename: string); var filen: string; d: PDoc; rst: PRstNode; code: PRope; begin filen := appendFileExt(filename, 'txt'); d := newDocumentor(filen); initIndexFile(d); rst := rstParse(readFile(filen), false, filen, 0, 1, d.hasToc); d.modDesc := renderRstToHtml(d, rst); code := genHtmlFile(d); writeRope(code, getOutFile(filename, HtmlExt)); generateIndex(d); end; // #FFD700 // #9f9b75 end.