Closed halloleo closed 3 years ago
I think it can work presently if you can pass the entire help
table as a value as in test/PassValues.nim
.
I admit this could be better than it is. When I have had some sleep, I will look into seeing how complex it would be to make it work how you are trying with just the V
in {K:V}
being a compile-time value. But let me know if doing the whole table like test/PassValues.nim
is enough for you.
I'll certainly try this. Thanks.
I just don't really understand why this problem occurs at all...My Nim basics are pretty shaky: Can table/dictionary structures not contain of variables? Does this happen because dispatch is a template and not a proc? Sorry, I guess this is probably a question better for a forum or similar...
Well, I can try to explain a little but if this is not enough then you should read the tutorial on Nim macro meta programming and dumpTree
and then come back here and read this again. :)
When you write a macro like dispatchGen
, it receives - at compile time - an abstract syntax tree representation of the program. I.e., the code "on the inside" of the macro parameter list is parsed but not yet compiled and delivered to the macro body parsed. The macro lets you intercede in the translation process by using the input AST to create an output AST.
In the beginning cligen
needed string literals or {}
"alist literals" (they are not really tables, but pure syntax such that {K1:V1, K2:V2} === [(K1,V1), (K2,V2)] and "alist" = "association list" is ancient Lisp terminology). but then because of https://github.com/c-blake/cligen/issues/110 I added a bunch of "forks/switches" where I test the input AST for either literalhood or symbolhood and get the string value cligen
needs to generate its help messages (really the whole CL parser-validator-helper) two different ways. I just check .kind == nnkSym
and if it's that I can getImpl
on the symbol, but otherwise if it's a string literal I get strVal
. That kind of thing. With macros you are sort of "creating syntax" or at least re-interpreting syntax.
So, I think I could do the same thing I did there, but I'd need to loop over each Vi and check if it is a literal or a symbol and extracting the needed value for the UI gets that much more complex. It's kind of a hassle (especially if I do it for both the help={}
and short={}
dictionaries). So, if doing the entire table like test/PassValue.nim
can work for you, I'd prefer to just have you do that and close this issue. That const
value that you create with toTable
is all done before the cligen
macro do anything. So, Nim proper has made it "a bit nicer" to use.
For example, even if I did this, complex expressions like a & b
concatenating strings would still not work "inline" in the { }
constructor, but it would in the earlier const
construct. It's also simpler to just tell people "literal or symbol" for the whole top-level parameter. (Not that I document this well...)
Since I haven't heard back from you but have checked that this works, I am closing this issue:
import cligen, strutils, tables
proc mycmd(args: seq[string]) = discard
const help = { "args": join(["a", "b", "c"], " ")}.toTable
dispatch(mycmd, help=help)
As I mentioned, making what you tried first work directly is probably do-able, but also increases explanation burden on the whole Nim API to UI generation. You could also layer that "args" value with an earlier const
if you like.
A maybe simpler way to explain the complexity you seemed confused about is that I think "binding to a symbol" and later doing getImpl
is probably the easiest to do "compile-time eval" with full expression generality. ("easiness" is often subjective). It also encourages cleaner client code in this case (IMO). A corollary of this is that we could probably bind constants internally inside cligen
stuff for each macro/template parameter, and then pass these to a deeper macro that just always does the getImpl
. If anyone wants to try a PR, I think it would be an good, non-trivial exercise for someone trying to learn Nim macro programming.
Full compile-time-expressional generality has not been requested much in the 5 years of cligen
s existence, though, and I have never felt the need in my own programs which include several dozens of use cases. E.g., for your args
case I would probably say cmdPath: seq[string]
instead of args: seq[string]
and then help["args"]
is almost superfluous (but still nice!).
Thanks for all the details and sorry for the delay!
Have read through your explanations. They makes sense, but I still need to get my head around this (very interesting) compile-time pre-execution model...
And I will try your suggestion begin of the week when I'm back at my Nim machine.
Yep, this works as advertised, @c-blake. For me that's good enough. :smile:
When I call
dispatch
the following way:and I run
mycmd --help
the output contains as the arguments the string "args_info" (i.e. not the content ofargs_info
but the characters "args_info").