Added jsondoc compiler switch

Outputs top-level exported information in JSON
This commit is contained in:
Erik O'Leary 2013-12-12 10:59:30 -06:00
commit aaf49947e9
3 changed files with 211 additions and 132 deletions

View file

@ -11,10 +11,10 @@
# semantic checking is done for the code. Cross-references are generated # semantic checking is done for the code. Cross-references are generated
# by knowing how the anchors are going to be named. # by knowing how the anchors are going to be named.
import import
ast, strutils, strtabs, options, msgs, os, ropes, idents, ast, strutils, strtabs, options, msgs, os, ropes, idents,
wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
importer, sempass2 importer, sempass2, json
type type
TSections = array[TSymKind, PRope] TSections = array[TSymKind, PRope]
@ -25,7 +25,7 @@ type
indexValFilename: string indexValFilename: string
PDoc* = ref TDocumentor PDoc* = ref TDocumentor
proc compilerMsgHandler(filename: string, line, col: int, proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.TMsgKind, arg: string) {.procvar.} = msgKind: rst.TMsgKind, arg: string) {.procvar.} =
# translate msg kind: # translate msg kind:
@ -41,69 +41,69 @@ proc compilerMsgHandler(filename: string, line, col: int,
of mwUnknownSubstitution: k = warnUnknownSubstitutionX of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported of mwUnsupportedLanguage: k = warnLanguageXNotSupported
GlobalError(newLineInfo(filename, line, col), k, arg) GlobalError(newLineInfo(filename, line, col), k, arg)
proc parseRst(text, filename: string, proc parseRst(text, filename: string,
line, column: int, hasToc: var bool, line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode = rstOptions: TRstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions, result = rstParse(text, filename, line, column, hasToc, rstOptions,
options.FindFile, compilerMsgHandler) options.FindFile, compilerMsgHandler)
proc newDocumentor*(filename: string, config: PStringTable): PDoc = proc newDocumentor*(filename: string, config: PStringTable): PDoc =
new(result) new(result)
initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex), initRstGenerator(result[], (if gCmd != cmdRst2Tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective}, options.gConfigVars, filename, {roSupportRawDirective},
options.FindFile, compilerMsgHandler) options.FindFile, compilerMsgHandler)
result.id = 100 result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) =
if gCmd != cmdRst2Tex: appf(dest, xml, args) if gCmd != cmdRst2Tex: appf(dest, xml, args)
else: appf(dest, tex, args) else: appf(dest, tex, args)
proc getVarIdx(varnames: openarray[string], id: string): int = proc getVarIdx(varnames: openarray[string], id: string): int =
for i in countup(0, high(varnames)): for i in countup(0, high(varnames)):
if cmpIgnoreStyle(varnames[i], id) == 0: if cmpIgnoreStyle(varnames[i], id) == 0:
return i return i
result = -1 result = -1
proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], proc ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string],
varvalues: openarray[PRope]): PRope = varvalues: openarray[PRope]): PRope =
var i = 0 var i = 0
var L = len(frmt) var L = len(frmt)
result = nil result = nil
var num = 0 var num = 0
while i < L: while i < L:
if frmt[i] == '$': if frmt[i] == '$':
inc(i) # skip '$' inc(i) # skip '$'
case frmt[i] case frmt[i]
of '#': of '#':
app(result, varvalues[num]) app(result, varvalues[num])
inc(num) inc(num)
inc(i) inc(i)
of '$': of '$':
app(result, "$") app(result, "$")
inc(i) inc(i)
of '0'..'9': of '0'..'9':
var j = 0 var j = 0
while true: while true:
j = (j * 10) + Ord(frmt[i]) - ord('0') j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i) inc(i)
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
num = j num = j
app(result, varvalues[j - 1]) app(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = "" var id = ""
while true: while true:
add(id, frmt[i]) add(id, frmt[i])
inc(i) inc(i)
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id) var idx = getVarIdx(varnames, id)
if idx >= 0: app(result, varvalues[idx]) if idx >= 0: app(result, varvalues[idx])
else: rawMessage(errUnkownSubstitionVar, id) else: rawMessage(errUnkownSubstitionVar, id)
of '{': of '{':
var id = "" var id = ""
inc(i) inc(i)
while frmt[i] != '}': while frmt[i] != '}':
if frmt[i] == '\0': rawMessage(errTokenExpected, "}") if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
add(id, frmt[i]) add(id, frmt[i])
inc(i) inc(i)
@ -124,17 +124,17 @@ proc genComment(d: PDoc, n: PNode): string =
var dummyHasToc: bool var dummyHasToc: bool
if n.comment != nil and startsWith(n.comment, "##"): if n.comment != nil and startsWith(n.comment, "##"):
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
toLineNumber(n.info), toColumn(n.info), toLineNumber(n.info), toColumn(n.info),
dummyHasToc, d.options + {roSkipPounds}), result) dummyHasToc, d.options + {roSkipPounds}), result)
proc genRecComment(d: PDoc, n: PNode): PRope = proc genRecComment(d: PDoc, n: PNode): PRope =
if n == nil: return nil if n == nil: return nil
result = genComment(d, n).toRope result = genComment(d, n).toRope
if result == nil: if result == nil:
if n.kind notin {nkEmpty..nkNilLit}: if n.kind notin {nkEmpty..nkNilLit}:
for i in countup(0, len(n)-1): for i in countup(0, len(n)-1):
result = genRecComment(d, n.sons[i]) result = genRecComment(d, n.sons[i])
if result != nil: return if result != nil: return
else: else:
n.comment = nil n.comment = nil
@ -158,10 +158,10 @@ proc extractDocComment*(s: PSym, d: PDoc = nil): string =
else: else:
result = n.comment.substr(2).replace("\n##", "\n").strip result = n.comment.substr(2).replace("\n##", "\n").strip
proc isVisible(n: PNode): bool = proc isVisible(n: PNode): bool =
result = false result = false
if n.kind == nkPostfix: if n.kind == nkPostfix:
if n.len == 2 and n.sons[0].kind == nkIdent: if n.len == 2 and n.sons[0].kind == nkIdent:
var v = n.sons[0].ident var v = n.sons[0].ident
result = v.id == ord(wStar) or v.id == ord(wMinus) result = v.id == ord(wStar) or v.id == ord(wMinus)
elif n.kind == nkSym: elif n.kind == nkSym:
@ -171,36 +171,36 @@ proc isVisible(n: PNode): bool =
result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported} result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported}
elif n.kind == nkPragmaExpr: elif n.kind == nkPragmaExpr:
result = isVisible(n.sons[0]) result = isVisible(n.sons[0])
proc getName(d: PDoc, n: PNode, splitAfter = -1): string = proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
case n.kind case n.kind
of nkPostfix: result = getName(d, n.sons[1], splitAfter) of nkPostfix: result = getName(d, n.sons[1], splitAfter)
of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter) of nkPragmaExpr: result = getName(d, n.sons[0], splitAfter)
of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter) of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
of nkIdent: result = esc(d.target, n.ident.s, splitAfter) of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
of nkAccQuoted: of nkAccQuoted:
result = esc(d.target, "`") result = esc(d.target, "`")
for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter)) for i in 0.. <n.len: result.add(getName(d, n[i], splitAfter))
result.add esc(d.target, "`") result.add esc(d.target, "`")
else: else:
internalError(n.info, "getName()") internalError(n.info, "getName()")
result = "" result = ""
proc getRstName(n: PNode): PRstNode = proc getRstName(n: PNode): PRstNode =
case n.kind case n.kind
of nkPostfix: result = getRstName(n.sons[1]) of nkPostfix: result = getRstName(n.sons[1])
of nkPragmaExpr: result = getRstName(n.sons[0]) of nkPragmaExpr: result = getRstName(n.sons[0])
of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName) of nkSym: result = newRstNode(rnLeaf, n.sym.renderDefinitionName)
of nkIdent: result = newRstNode(rnLeaf, n.ident.s) of nkIdent: result = newRstNode(rnLeaf, n.ident.s)
of nkAccQuoted: of nkAccQuoted:
result = getRstName(n.sons[0]) result = getRstName(n.sons[0])
for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text) for i in 1 .. <n.len: result.text.add(getRstName(n[i]).text)
else: else:
internalError(n.info, "getRstName()") internalError(n.info, "getRstName()")
result = nil result = nil
proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
if not isVisible(nameNode): return if not isVisible(nameNode): return
var name = toRope(getName(d, nameNode)) var name = toRope(getName(d, nameNode))
var result: PRope = nil var result: PRope = nil
var literal = "" var literal = ""
@ -208,73 +208,89 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
var comm = genRecComment(d, n) # call this here for the side-effect! var comm = genRecComment(d, n) # call this here for the side-effect!
var r: TSrcGen var r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
while true: while true:
getNextTok(r, kind, literal) getNextTok(r, kind, literal)
case kind case kind
of tkEof: of tkEof:
break break
of tkComment: of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh: of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[toRope(literal)]) [toRope(literal)])
of tkOpr: of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit: of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>", dispA(result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [toRope(esc(d.target, literal))]) "\\spanStringLit{$1}", [toRope(esc(d.target, literal))])
of tkCharLit: of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit: of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>", dispA(result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanDecNumber{$1}", [toRope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit: of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>", dispA(result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))]) "\\spanFloatNumber{$1}", [toRope(esc(d.target, literal))])
of tkSymbol: of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>", dispA(result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [toRope(esc(d.target, literal))]) "\\spanIdentifier{$1}", [toRope(esc(d.target, literal))])
of tkSpaces, tkInvalid: of tkSpaces, tkInvalid:
app(result, literal) app(result, literal)
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon, tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr: tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[toRope(esc(d.target, literal))]) [toRope(esc(d.target, literal))])
inc(d.id) inc(d.id)
app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
["name", "header", "desc", "itemID"], ["name", "header", "desc", "itemID"],
[name, result, comm, toRope(d.id)])) [name, result, comm, toRope(d.id)]))
app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
["name", "header", "desc", "itemID"], [ ["name", "header", "desc", "itemID"], [
toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)])) toRope(getName(d, nameNode, d.splitAfter)), result, comm, toRope(d.id)]))
setIndexTerm(d[], $d.id, getName(d, nameNode)) setIndexTerm(d[], $d.id, getName(d, nameNode))
proc checkForFalse(n: PNode): bool = proc genJSONItem(d: PDoc, n, nameNode: PNode, k: TSymKind): PJsonNode =
if not isVisible(nameNode): return
var
name = getName(d, nameNode)
comm = genRecComment(d, n).ropeToStr()
r: TSrcGen
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k) }
if comm != nil and comm != "":
result["description"] = %comm
if r.buf != nil:
result["code"] = %r.buf
proc checkForFalse(n: PNode): bool =
result = n.kind == nkIdent and IdentEq(n.ident, "false") result = n.kind == nkIdent and IdentEq(n.ident, "false")
proc traceDeps(d: PDoc, n: PNode) = proc traceDeps(d: PDoc, n: PNode) =
const k = skModule const k = skModule
if d.section[k] != nil: app(d.section[k], ", ") if d.section[k] != nil: app(d.section[k], ", ")
dispA(d.section[k], dispA(d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>", "<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [toRope(getModuleName(n))]) "$1", [toRope(getModuleName(n))])
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n)) of nkCommentStmt: app(d.modDesc, genComment(d, n))
of nkProcDef: of nkProcDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skProc) genItem(d, n, n.sons[namePos], skProc)
of nkMethodDef: of nkMethodDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skMethod) genItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef: of nkIteratorDef:
when useEffectSystem: documentRaises(n) when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skIterator) genItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
@ -284,27 +300,69 @@ proc generateDoc*(d: PDoc, n: PNode) =
genItem(d, n, n.sons[namePos], skConverter) genItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt: if n.sons[i].kind != nkCommentStmt:
# order is always 'type var let const': # order is always 'type var let const':
genItem(d, n.sons[i], n.sons[i].sons[0], genItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection))) succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i])
of nkWhenStmt: of nkWhenStmt:
# generate documentation for the first branch only: # generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]): if not checkForFalse(n.sons[0].sons[0]):
generateDoc(d, lastSon(n.sons[0])) generateDoc(d, lastSon(n.sons[0]))
of nkImportStmt: of nkImportStmt:
for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
else: nil else: nil
proc genSection(d: PDoc, kind: TSymKind) = proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode =
case n.kind
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
result = %{ "comment": %stripped }
of nkProcDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skProc)
of nkMethodDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef:
result = genJSONItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef:
result = genJSONItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef:
when useEffectSystem: documentRaises(n)
result = genJSONItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt:
# order is always 'type var let const':
result = genJSONItem(d, n.sons[i], n.sons[i].sons[0],
succ(skType, ord(n.kind)-ord(nkTypeSection)))
of nkStmtList:
var elem = jArray
if elem == nil: elem = newJArray()
for i in countup(0, sonsLen(n) - 1):
var r = generateJson(d, n.sons[i], elem)
if r != nil:
elem.add(r)
if result == nil: result = elem
of nkWhenStmt:
# generate documentation for the first branch only:
if not checkForFalse(n.sons[0].sons[0]) and jArray != nil:
discard generateJson(d, lastSon(n.sons[0]), jArray)
else: nil
proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [ const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
"Iterators", "Converters", "Macros", "Templates" "Iterators", "Converters", "Macros", "Templates"
] ]
if d.section[kind] == nil: return if d.section[kind] == nil: return
var title = sectionNames[kind].toRope var title = sectionNames[kind].toRope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
@ -313,7 +371,7 @@ proc genSection(d: PDoc, kind: TSymKind) =
"sectionid", "sectionTitle", "sectionTitleID", "content"], [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]]) ord(kind).toRope, title, toRope(ord(kind) + 50), d.toc[kind]])
proc genOutFile(d: PDoc): PRope = proc genOutFile(d: PDoc): PRope =
var var
code, content: PRope code, content: PRope
title = "" title = ""
@ -321,7 +379,7 @@ proc genOutFile(d: PDoc): PRope =
var tmp = "" var tmp = ""
renderTocEntries(d[], j, 1, tmp) renderTocEntries(d[], j, 1, tmp)
var toc = tmp.toRope var toc = tmp.toRope
for i in countup(low(TSymKind), high(TSymKind)): for i in countup(low(TSymKind), high(TSymKind)):
genSection(d, i) genSection(d, i)
app(toc, d.toc[i]) app(toc, d.toc[i])
if toc != nil: if toc != nil:
@ -329,30 +387,30 @@ proc genOutFile(d: PDoc): PRope =
for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i])
if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] if d.meta[metaTitle].len != 0: title = d.meta[metaTitle]
else: title = "Module " & extractFilename(changeFileExt(d.filename, "")) else: title = "Module " & extractFilename(changeFileExt(d.filename, ""))
let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc" let bodyname = if d.hasToc: "doc.body_toc" else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"], "tableofcontents", "moduledesc", "date", "time", "content"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), code]) toRope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions: if optCompileOnly notin gGlobalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!? # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope, toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
d.meta[metaVersion].toRope]) d.meta[metaVersion].toRope])
else: else:
code = content code = content
result = code result = code
proc generateIndex*(d: PDoc) = proc generateIndex*(d: PDoc) =
if optGenIndex in gGlobalOptions: if optGenIndex in gGlobalOptions:
writeIndexFile(d[], splitFile(options.outFile).dir / writeIndexFile(d[], splitFile(options.outFile).dir /
splitFile(d.filename).name & indexExt) splitFile(d.filename).name & indexExt)
proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
var content = genOutFile(d) var content = genOutFile(d)
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
writeRope(stdout, content) writeRope(stdout, content)
@ -361,7 +419,7 @@ proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
proc CommandDoc*() = proc CommandDoc*() =
var ast = parseFile(gProjectMainIdx) var ast = parseFile(gProjectMainIdx)
if ast == nil: return if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars) var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true d.hasToc = true
generateDoc(d, ast) generateDoc(d, ast)
@ -388,12 +446,26 @@ proc CommandRst2TeX*() =
splitter = "\\-" splitter = "\\-"
CommandRstAux(gProjectFull, TexExt) CommandRstAux(gProjectFull, TexExt)
proc CommandJSON*() =
var ast = parseFile(gProjectMainIdx)
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
d.hasToc = true
var json = generateJson(d, ast)
var content = newRope(pretty(json))
if optStdout in gGlobalOptions:
writeRope(stdout, content)
else:
echo getOutFile(gProjectFull, JsonExt)
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
proc CommandBuildIndex*() = proc CommandBuildIndex*() =
var content = mergeIndexes(gProjectFull).toRope var content = mergeIndexes(gProjectFull).toRope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version"],
["Index".toRope, nil, nil, toRope(getDateStr()), ["Index".toRope, nil, nil, toRope(getDateStr()),
toRope(getClockStr()), content, nil, nil]) toRope(getClockStr()), content, nil, nil])
writeRope(code, getOutFile("theindex", HtmlExt)) writeRope(code, getOutFile("theindex", HtmlExt))

View file

@ -9,8 +9,8 @@
# implements the command dispatcher and several commands # implements the command dispatcher and several commands
import import
llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
os, condsyms, rodread, rodwrite, times, os, condsyms, rodread, rodwrite, times,
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, jsgen, json, nversion, cgen, jsgen, json, nversion,
@ -98,7 +98,7 @@ proc CommandCompileToC =
# rodread.rodcompilerProcs # rodread.rodcompilerProcs
# rodread.gTypeTable # rodread.gTypeTable
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed? # semthreads.computed?
# #
@ -166,7 +166,7 @@ proc commandEval(exp: string) =
proc CommandPrettyOld = proc CommandPrettyOld =
var projectFile = addFileExt(mainCommandArg(), NimExt) var projectFile = addFileExt(mainCommandArg(), NimExt)
var module = parseFile(projectFile.fileInfoIdx) var module = parseFile(projectFile.fileInfoIdx)
if module != nil: if module != nil:
renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt)) renderModule(module, getOutFile(mainCommandArg(), "pretty." & NimExt))
proc CommandPretty = proc CommandPretty =
@ -175,24 +175,24 @@ proc CommandPretty =
registerPass(prettyPass) registerPass(prettyPass)
compileProject() compileProject()
pretty.overwriteFiles() pretty.overwriteFiles()
proc CommandScan = proc CommandScan =
var f = addFileExt(mainCommandArg(), nimExt) var f = addFileExt(mainCommandArg(), nimExt)
var stream = LLStreamOpen(f, fmRead) var stream = LLStreamOpen(f, fmRead)
if stream != nil: if stream != nil:
var var
L: TLexer L: TLexer
tok: TToken tok: TToken
initToken(tok) initToken(tok)
openLexer(L, f, stream) openLexer(L, f, stream)
while true: while true:
rawGetTok(L, tok) rawGetTok(L, tok)
PrintTok(tok) PrintTok(tok)
if tok.tokType == tkEof: break if tok.tokType == tkEof: break
CloseLexer(L) CloseLexer(L)
else: else:
rawMessage(errCannotOpenFile, f) rawMessage(errCannotOpenFile, f)
proc CommandSuggest = proc CommandSuggest =
if isServing: if isServing:
# XXX: hacky work-around ahead # XXX: hacky work-around ahead
@ -246,7 +246,7 @@ proc resetMemory =
for i in low(buckets)..high(buckets): for i in low(buckets)..high(buckets):
buckets[i] = nil buckets[i] = nil
idAnon = nil idAnon = nil
# XXX: clean these global vars # XXX: clean these global vars
# ccgstmts.gBreakpoints # ccgstmts.gBreakpoints
# ccgthreadvars.nimtv # ccgthreadvars.nimtv
@ -262,7 +262,7 @@ proc resetMemory =
# rodread.rodcompilerProcs # rodread.rodcompilerProcs
# rodread.gTypeTable # rodread.gTypeTable
# rodread.gMods # rodread.gMods
# !! ropes.cache # !! ropes.cache
# semthreads.computed? # semthreads.computed?
# #
@ -289,7 +289,7 @@ const
proc MainCommand* = proc MainCommand* =
when SimiluateCaasMemReset: when SimiluateCaasMemReset:
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
# In "nimrod serve" scenario, each command must reset the registered passes # In "nimrod serve" scenario, each command must reset the registered passes
clearPasses() clearPasses()
gLastCmdTime = epochTime() gLastCmdTime = epochTime()
@ -301,7 +301,7 @@ proc MainCommand* =
passes.gIncludeFile = includeModule passes.gIncludeFile = includeModule
passes.gImportModule = importModule passes.gImportModule = importModule
case command.normalize case command.normalize
of "c", "cc", "compile", "compiletoc": of "c", "cc", "compile", "compiletoc":
# compile means compileToC currently # compile means compileToC currently
gCmd = cmdCompileToC gCmd = cmdCompileToC
wantMainModule() wantMainModule()
@ -325,13 +325,13 @@ proc MainCommand* =
when hasTinyCBackend: when hasTinyCBackend:
extccomp.setCC("tcc") extccomp.setCC("tcc")
CommandCompileToC() CommandCompileToC()
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
of "js", "compiletojs": of "js", "compiletojs":
gCmd = cmdCompileToJS gCmd = cmdCompileToJS
wantMainModule() wantMainModule()
CommandCompileToJS() CommandCompileToJS()
of "compiletollvm": of "compiletollvm":
gCmd = cmdCompileToLLVM gCmd = cmdCompileToLLVM
wantMainModule() wantMainModule()
when has_LLVM_Backend: when has_LLVM_Backend:
@ -353,21 +353,27 @@ proc MainCommand* =
wantMainModule() wantMainModule()
DefineSymbol("nimdoc") DefineSymbol("nimdoc")
CommandDoc2() CommandDoc2()
of "rst2html": of "rst2html":
gCmd = cmdRst2html gCmd = cmdRst2html
LoadConfigs(DocConfig) LoadConfigs(DocConfig)
wantMainModule() wantMainModule()
CommandRst2Html() CommandRst2Html()
of "rst2tex": of "rst2tex":
gCmd = cmdRst2tex gCmd = cmdRst2tex
LoadConfigs(DocTexConfig) LoadConfigs(DocTexConfig)
wantMainModule() wantMainModule()
CommandRst2TeX() CommandRst2TeX()
of "jsondoc":
gCmd = cmdDoc
LoadConfigs(DocConfig)
wantMainModule()
DefineSymbol("nimdoc")
CommandJSON()
of "buildindex": of "buildindex":
gCmd = cmdDoc gCmd = cmdDoc
LoadConfigs(DocConfig) LoadConfigs(DocConfig)
CommandBuildIndex() CommandBuildIndex()
of "gendepend": of "gendepend":
gCmd = cmdGenDepend gCmd = cmdGenDepend
wantMainModule() wantMainModule()
CommandGenDepend() CommandGenDepend()
@ -400,16 +406,16 @@ proc MainCommand* =
gCmd = cmdCheck gCmd = cmdCheck
wantMainModule() wantMainModule()
CommandCheck() CommandCheck()
of "parse": of "parse":
gCmd = cmdParse gCmd = cmdParse
wantMainModule() wantMainModule()
discard parseFile(gProjectMainIdx) discard parseFile(gProjectMainIdx)
of "scan": of "scan":
gCmd = cmdScan gCmd = cmdScan
wantMainModule() wantMainModule()
CommandScan() CommandScan()
MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!") MsgWriteln("Beware: Indentation tokens depend on the parser\'s state!")
of "i": of "i":
gCmd = cmdInteractive gCmd = cmdInteractive
CommandInteractive() CommandInteractive()
of "e": of "e":
@ -427,11 +433,11 @@ proc MainCommand* =
of "serve": of "serve":
isServing = true isServing = true
gGlobalOptions.incl(optCaasEnabled) gGlobalOptions.incl(optCaasEnabled)
msgs.gErrorMax = high(int) # do not stop after first error msgs.gErrorMax = high(int) # do not stop after first error
serve(MainCommand) serve(MainCommand)
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
if (msgs.gErrorCounter == 0 and if (msgs.gErrorCounter == 0 and
gCmd notin {cmdInterpret, cmdRun, cmdDump} and gCmd notin {cmdInterpret, cmdRun, cmdDump} and
gVerbosity > 0): gVerbosity > 0):

View file

@ -132,6 +132,7 @@ const
NimExt* = "nim" NimExt* = "nim"
RodExt* = "rod" RodExt* = "rod"
HtmlExt* = "html" HtmlExt* = "html"
JsonExt* = "json"
TexExt* = "tex" TexExt* = "tex"
IniExt* = "ini" IniExt* = "ini"
DefaultConfig* = "nimrod.cfg" DefaultConfig* = "nimrod.cfg"