In the code below I can use makeProc to generate code as I demonstrate with x1 and x2. But I want to use a loop, something like the last couple of disabled lines, how do that?
$cat makeproc.nim
template makeProc(name: untyped) =
var `name Counter` {.inject.}: int
proc name(param: int) =
`name Counter` += 1
echo "param=" & $param & " counter=" & $`name Counter`
makeProc(x1)
x1(1)
x1(2)
x1(3)
echo "x1Counter=", x1Counter
makeProc(x2)
x2(4)
x2(5)
echo "x2Counter=", x2Counter
# Use a loop to create 3 procs, y1, y2, y3
when false:
for i in 1..3:
makeProc(y & i)
Here is the output:
$ nim c -r --hints:off makeproc.nim
CC: makeproc
[Linking]
/home/wink/prgs/nim/nim-exploration/makeproc
param=1 counter=1
param=2 counter=2
param=3 counter=3
x1Counter=3
param=4 counter=1
param=5 counter=2
x2Counter=2
So this works:
$ cat makeprocs.nim
import macros, strutils
macro makeProcs(n: varargs[expr]): stmt =
## parameter n[0] is the base name and n[1] is the
## number of procs to create of the form baseName & $i.
## So makeProcs("def", "3") creates:
## proc def1() =
## echo("my name=def1")
## proc def2() =
## echo("my name=def2")
## proc def3() =
## echo("my name=def3")
var
baseName: string = $n[0]
num: int = parseInt($n[1])
echo "makeP: n.len=", n.len, " baseName=", baseName, " num=", num
result = newStmtList()
for i in 1..num:
var
name = newIdentNode(baseName & $i)
procParams = [newEmptyNode()]
procBody = newStmtList()
procBody.add(newCall("echo", newStrLitNode("my name=" & $name)))
result.add(newProc(name, procParams, procBody))
makeProcs("def", "3")
def1()
def2()
def3()
The output is:
$ ./makeprocs
my name=def1
my name=def2
my name=def3
But I'd like to invoke the macro with:
makeProcs("def", 3)
or
makeProcs(def, 3)
Any advice on how I might do either or both of those?For your bottom option, you can use the {.immediate.} syntax, which will tell the compiler not to evaluate the expressions inside of the macro argument list.
import macros, strutils
macro makeProcs(name: expr, number: expr): stmt {.immediate.} = # immediate pragma with two arguments. Not sure how to use varargs here
## parameter n[0] is the base name and n[1] is the
## number of procs to create of the form baseName & $i.
## So makeProcs("def", "3") creates:
## proc def1() =
## echo("my name=def1")
## proc def2() =
## echo("my name=def2")
## proc def3() =
## echo("my name=def3")
var
baseName: string = $name
num: int = parseInt($number.toStrLit()) # Convert the number to a string literal node, then convert that to a string to convert it to an int :P
echo "makeP: baseName=", baseName, " num=", num
result = newStmtList()
for i in 1..num:
var
name = newIdentNode(baseName & $i)
procParams = [newEmptyNode()]
procBody = newStmtList()
procBody.add(newCall("echo", newStrLitNode("my name=" & $name)))
result.add(newProc(name, procParams, procBody))
makeProcs(def, 3)
def1()
def2()
def3()
The above code does what you want. I couldn't figure out how to use the varargs type with the {.immediate.} pragma, but it's possible that the compiler ignores these things.
Thanks, I'd tried something like that but couldn't get it to compile. What I hadn't done correctly was figure out the "$name/$number.toStrList()":
baseName: string = $name
num: int = parseInt($number.toStrLit())
Also, it does work if you pass explicit types to makeProcs rather than expr, is there a reason to choose expr over explicit types (sting/int)?
macro makeProcs(name: string, number: int): stmt {.immediate.} =
## parameter n[0] is the base name and n[1] is the
## number of procs to create of the form baseName & $i.
## So makeProcs("def", "3") creates:
## proc def1() =
## echo("my name=def1")
## proc def2() =
## echo("my name=def2")
## proc def3() =
## echo("my name=def3")
var
baseName: string = $name
num: int = parseInt($number.toStrLit())
echo "makeP: baseName=", baseName, " num=", num
result = newStmtList()
for i in 1..num:
var
name = newIdentNode(baseName & $i)
procParams = [newEmptyNode()]
procBody = newStmtList()
procBody.add(newCall("echo", newStrLitNode("my name=" & $name)))
result.add(newProc(name, procParams, procBody))
makeProcs(def, 3)
def1()
def2()
def3()