diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2018-09-02 22:56:26 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2018-09-02 22:56:26 +0200 |
commit | 1948eadc24c8964b581240dc841e4eb369a1ad36 (patch) | |
tree | 5c9950326820b1ec845f4041c60ef14ed3abef8c /compiler | |
parent | 4cf704bb3e26e002c5ff479a01fffcd3e33f1cb2 (diff) | |
download | Nim-1948eadc24c8964b581240dc841e4eb369a1ad36.tar.gz |
change runnableExamples implementation; fixes #8641; fixes #7135; runnableExamples works for templates and generics
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/docgen.nim | 59 | ||||
-rw-r--r-- | compiler/docgen2.nim | 1 | ||||
-rw-r--r-- | compiler/sem.nim | 23 | ||||
-rw-r--r-- | compiler/semdata.nim | 1 | ||||
-rw-r--r-- | 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, "<span class=\"Other\">$1</span>", "\\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..<n.safeLen: extractImports(n[i], result) + +proc prepareExamples(d: PDoc; n: PNode) = + let inp = toFullPath(d.conf, n.info) + if d.runnableExamples == nil: + d.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + d.runnableExamples.info = n.info + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: d.runnableExamples.add imp + d.runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon) + +proc isRunnableExample(n: PNode): bool = + # Templates and generics don't perform symbol lookups. + result = n.kind == nkSym and n.sym.magic == mRunnableExamples or + n.kind == nkIdent and n.ident.s == "runnableExamples" + proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) = case n.kind of nkCallKinds: - if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and + if isRunnableExample(n[0]) and n.len >= 2 and n.lastSon.kind == nkStmtList: + prepareExamples(d, n) dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\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..<n.safeLen: extractImports(n[i], result) - proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! # DON'T forget to update ast.SpecialSemMagics if you add a magic here! @@ -2033,16 +2026,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = magicsAfterOverloadResolution(c, result, flags) of mRunnableExamples: if c.config.cmd == cmdDoc and n.len >= 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 |