When I have a procedure like this
proc solver:string =
## Solves all the problems of the world
return 42
How can I access the Documentation Comment "Solves all the problems of the world" in the code following the procedure. Looking for something like the solver.__doc__ property in Python...
Thanks for any pointers!
You can do so by creating a macro that inspects the syntax tree of your procedure and extracts doc comments from it:
import std/macros
import std/strutils
# the parameter has to be `typed` because we need to access the proc's
# implementation
macro doc(procedure: typed): untyped =
# just some code to check that the input is valid
procedure.expectKind(nnkSym)
if procedure.symKind != nskProc:
error("procedure expected", procedure)
# now we take the procedure's implementation with getImpl.
# note that this can only be done if `procedure` is `typed`!
let
impl = procedure.getImpl
# then we can extract all doc comments and runnableExamples from its
# body using the following procedure:
docs = impl.body.extractDocCommentsAndRunnables
# now we can catenate all doc comments into one large string
# we'll also separate each doc comment with a line break
var doc: string
for element in docs:
if element.kind == nnkCommentStmt:
doc.addSep("\n", startLen = 1)
doc.add($element)
# with that said and done, we can finally return the syntax tree for a
# string literal with our concatenated doc comments
result = newLit(doc)
proc solver: int =
## Solves all the problems of the world
return 42
# usage:
echo solver.doc
Bookmarking this now for the eventual "underhanded Nim Code Contest".
Seriously, be very careful - comment changes executable path taken is NOT a good idea. As long as it's just for string content, it's probably ok, but it's a slippery slope. These strings should somehow be tainted so they don't accidentally effect execution path.
Call me old fashioned, but I think that it's a bad idea that a comment can change the semantics of code. Nim has excellent annotation support through pragmas, e.g. Norm[0] uses pragmas to mark unique and foreign key constraints.
I would prefer a recommendation to leave comments for doc extraction tools, and only use pragmas to annotate programmatically (and change semantics).
Re: the edit - It's right at the start of the README. I just tried to make it more clear. The default program help doc is lifted from the doc comment of the wrapped proc, just as here (but directly off of getImpl since A) extractDocCommentsAndRunnables did not exist when I wrote cligen and B) cligen also needs the proc signature to do its dance.)
I actually support CL-user opt-in minimal rST styling, too..single|double backticks and single..triple asterisks, with user-directed binding of that stuff to ANSI SGR color/font escapes. (I don't do tables/links/etc. I looked into the stdlib rST stuff but it did not seem easily re-targettable to terminal text in addition to the HTML/LaTeX targets. rST/markdown are also designed to look good or at least be easily interpretable in ASCII/unrendered.) So, fancily formatted rST doc comments should be sorta ok dual-rendering as either nim doc for the API docs or as --help text for the program docs (but CLI author can override with a dispatch(foo, doc="stuff") if there's trouble). All that said, most cligen users seem to write this doc comment specially targeted at their CLI users, not as dual purpose API/CL docs.
It seems possible I misinterpreted your complaint as compiler passes being better than macros for doc extraction, but that seems kind of neglecting of Nim's superpowers. So, maybe a bias from another programming language. :-) But neither of these approaches really change "semantics". Even runnable examples only get compiled in a special program text processing mode. So, we may be disagreeing about the "semantics of semantics" which is often a good place to stop arguing... :-)