From 1948eadc24c8964b581240dc841e4eb369a1ad36 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Sep 2018 22:56:26 +0200 Subject: [PATCH] change runnableExamples implementation; fixes #8641; fixes #7135; runnableExamples works for templates and generics --- compiler/docgen.nim | 59 ++++++++++++++++++++++++++++++++++++++++++- compiler/docgen2.nim | 1 + compiler/sem.nim | 23 ----------------- compiler/semdata.nim | 1 - compiler/semexprs.nim | 28 ++++++++------------ 5 files changed, 70 insertions(+), 42 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3842f0ad4..2910f2253 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -31,6 +31,7 @@ type isPureRst: bool conf*: ConfigRef cache*: IdentCache + runnableExamples*: PNode PDoc* = ref TDocumentor ## Alias to type less. @@ -284,11 +285,60 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe dispA(d.conf, result, "$1", "\\spanOther{$1}", [rope(esc(d.target, literal))]) +proc testExamples*(d: PDoc) = + if d.runnableExamples == nil: return + let outputDir = d.conf.getNimcacheDir / "runnableExamples" + createDir(outputDir) + let inp = toFullPath(d.conf, d.runnableExamples.info) + let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim") + let nimcache = outp.changeFileExt"" & "_nimcache" + renderModule(d.runnableExamples, inp, outp, conf = d.conf) + let backend = if isDefined(d.conf, "js"): "js" + elif isDefined(d.conf, "cpp"): "cpp" + elif isDefined(d.conf, "objc"): "objc" + else: "c" + if os.execShellCmd(os.getAppFilename() & " " & backend & + " --nimcache:" & nimcache & " -r " & outp) != 0: + quit "[Examples] failed: see " & outp + else: + # keep generated source file `outp` to allow inspection. + rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp]) + removeFile(outp.changeFileExt(ExeExt)) + try: + removeDir(nimcache) + except OSError: + discard + +proc extractImports(n: PNode; result: PNode) = + if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: + result.add copyTree(n) + n.kind = nkEmpty + return + for i in 0..= 2 and n.lastSon.kind == nkStmtList: + prepareExamples(d, n) dispA(d.conf, dest, "\n

$1

\n", "\n\\textbf{$1}\n", [rope"Examples:"]) inc d.listingCounter @@ -627,6 +677,10 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkImportStmt: for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) + of nkCallKinds: + var comm: Rope = nil + getAllRunnableExamples(d, n, comm) + if comm > nil: add(d.modDesc, comm) else: discard proc add(d: PDoc; j: JsonNode) = @@ -819,6 +873,7 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) = generateDoc(d, ast) writeOutput(d, conf.projectFull, HtmlExt) generateIndex(d) + testExamples(d) proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) = var filen = addFileExt(filename, "txt") @@ -853,6 +908,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) d.modDesc = rope(modDesc) writeOutput(d, filename, outExt) generateIndex(d) + testExamples(d) proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = commandRstAux(cache, conf, conf.projectFull, HtmlExt) @@ -876,6 +932,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) = let filename = getOutFile(conf, conf.projectFull, JsonExt) if not writeRope(content, filename): rawMessage(conf, errCannotOpenFile, filename) + testExamples(d) proc commandTags*(cache: IdentCache, conf: ConfigRef) = var ast = parseFile(conf.projectMainIdx, cache, conf) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 068c47bb3..aa61dbd27 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -30,6 +30,7 @@ template closeImpl(body: untyped) {.dirty.} = body try: generateIndex(g.doc) + testExamples(g.doc) except IOError: discard diff --git a/compiler/sem.nim b/compiler/sem.nim index 6128c02d1..7a83c3079 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -607,28 +607,6 @@ proc myProcess(context: PPassContext, n: PNode): PNode = #if c.config.cmd == cmdIdeTools: findSuggest(c, n) rod.storeNode(c.graph, c.module, result) -proc testExamples(c: PContext) = - let outputDir = c.config.getNimcacheDir / "runnableExamples" - createDir(outputDir) - let inp = toFullPath(c.config, c.module.info) - let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim") - let nimcache = outp.changeFileExt"" & "_nimcache" - renderModule(c.runnableExamples, inp, outp, conf = c.config) - let backend = if isDefined(c.config, "js"): "js" - elif isDefined(c.config, "cpp"): "cpp" - elif isDefined(c.config, "objc"): "objc" - else: "c" - if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0: - quit "[Examples] failed: see " & outp - else: - # keep generated source file `outp` to allow inspection. - rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp]) - removeFile(outp.changeFileExt(ExeExt)) - try: - removeDir(nimcache) - except OSError: - discard - proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) if c.config.cmd == cmdIdeTools and not c.suggestionsMade: @@ -644,7 +622,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = popOwner(c) popProcCon(c) storeRemaining(c.graph, c.module) - if c.runnableExamples != nil: testExamples(c) const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 2e59c07b0..6d6627690 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -141,7 +141,6 @@ type # the generic type has been constructed completely. See # tests/destructor/topttree.nim for an example that # would otherwise fail. - runnableExamples*: PNode template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 04e76102b..ce953f17c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1953,13 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info -proc extractImports(n: PNode; result: PNode) = - if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: - result.add copyTree(n) - n.kind = nkEmpty - return - for i in 0..= 2 and n.lastSon.kind == nkStmtList: - if sfMainModule in c.module.flags: - let inp = toFullPath(c.config, c.module.info) - if c.runnableExamples == nil: - c.runnableExamples = newTree(nkStmtList, - newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) - let imports = newTree(nkStmtList) - var saved_lastSon = copyTree n.lastSon - extractImports(saved_lastSon, imports) - for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon) + when false: + if sfMainModule in c.module.flags: + let inp = toFullPath(c.config, c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: c.runnableExamples.add imp + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon) result = setMs(n, s) else: result = c.graph.emptyNode