I have a scenario where I have an enum with a large number of values that is being generated by a macro from some external data. From the value of the enum, I have macro code that builds up the correct body of each ''of'' branch in the case statement.
Imagine something like this being generated by a macro, except with a lot more values in the enum.
type
MyTest = mtSomething, mtOther, mtEtc
proc doSomethingWithMyTest(mt: MyTest) =
case mt:
of mtSomething:
discard "Some logic for mtSomething here"
of mtOther:
discard "Some other logic here"
of mtEtc:
discard "Etc, etc. etc"
That all works perfectly, but I was wondering if I could simplify things a bit by having a template for defining the ''doSomethingWithMyTest'' proc.
The main reason would just be for ease of reading since the real proc with the case statement in it has a bit more code to it. I generate the entire proc right now via the API, but I was wondering if there was any way to do something like this
template makeMyProc() =
proc doSomethingWithMyTest(mt: MyTest) =
case mt:
`something to insert the of branches here`
Can I generate the ''of'' branches with the existing logic and then pass that into the template somehow?
It's not a big deal since the macro API works well, but I was curious if this would be possible.
Thanks
Oh yeah, I should probably mention one of the things that I tried when I was experimenting with this.
template makeSkeleton() =
## Generates the skeleton AST for this proc.
## The caller is expected to fill in the case statement
proc doSomethingWithMyTest(mt: MyTest) =
case mt:
macro makeTheWholeProc() : stmt =
result = getAST(makeSkeleton())
let caseStmt = result[^1][^1][0]
# now use macro API to update the caseStmt NimNode with the ''of'' branches
Grabbing the AST from a template with just the stub of the case statement does work, but this seems a bit fragile.
Maybe this approach would be OK if I created some helper procs for walking the AST to find the case statement in more robust way than relying on indexes like this example is doing.
Apparently, inputting standalone of branches into a template does not work. You could do something like this, although I do not see much of an advantage:
type MyTest = enum
mtSomething, mtOther, mtEtc
template makeMyProc(actionSomething, actionOther, actionEtc: stmt): stmt =
proc doSomethingWithMyTest(mt: MyTest) =
case mt
of mtSomething: actionSomething
of mtOther: actionOther
of mtEtc: actionEtc
makeMyProc() do:
discard # mtSomething
do:
discard # mtOther
do:
discard # mtEtc
Thanks for the input flyx. I hadn't realized that you could pass more than one block to a template, so that's useful to know. I can't use it in this scenario since the values for the enum are generated from external data, but I can think of some other places where it might be handy.
By the way, in case anyone else is interested in this sort of thing, here is a helper proc that I came up with for inserting the of branches into the case statement via the API.
proc insertOfBranch(caseStmt, ofBranch: NimNode) {.compileTime.} =
## Adds ''ofBranch'' to the ''caseStmt''. The ''ofBranch'' is inserted
## at the very end of the list, prior to an ''else'' branch, if there is one
caseStmt.expectKind(nnkCaseStmt)
ofBranch.expectKind(nnkOfBranch)
if caseStmt.len <= 1:
caseStmt.add(ofBranch)
else:
var insertPos = caseStmt.len
if caseStmt[^1].kind == nnkElse:
insertPos = caseStmt.len - 1
caseStmt.insert(insertPos, ofBranch)