It's incredibly easy to accidentally forget to copy a NimNode inside a macro.
In this example:
macro doStuffTo1to5(a : untyped, body : untyped) : untyped =
result = newStmtList()
for i in 1..5:
let i =
newLit(i)
let
blockStmtList = nnkStmtList.newTree()
blockStmtList.add quote do:
const `a` = `i`
blockStmtList.add body
#should be
#blockStmtList.add body.copy
let
blockStmt = nnkBlockStmt.newTree(newEmptyNode(),blockStmtList)
result.add blockStmt
doStuffTo1to5(y):
echo y
1 will be printed 5 times instead of 1 to 5 because body was not copied. This kind of bug is even harder to catch because if I had added body inside quote do: it would automatically do the copy for me encouraging the bad practice of forgetting to copy in future.
I only ask because I was thinking of making my own version of treeRepr that points out this type of bug and makes it real obvious. Having a version of treeRepr that automatically pointed out these type of bugs would be especially helpful for people new to Nim that don't understand the implementation details behind Nim.
On another note I wouldn't mind if treeRepr had an optional parameter that shortened openSymChoice and closedSymChoices to one element each as they tend to clutter output and most of the time all the choices have the same name anyway. Maybe even make that the default with the option to disable it.
should be blockStmtList.add body.copy
Well, why?
Run the code.
The point of the code was to create a new const variable for each iteration of the loop in the macro. If you don't copy body it only creates one variable resulting in the output : 1 1 1 1 1 The intended output is 1 2 3 4 5.
This is a toy example written to show a potential source of bugs for new Nim users.
Heres a better example
import macros
macro tester() : untyped =
let
x = ident("test")
varX = quote do:
var `x` = 5
varXNoCopy = quote do:
var `x` = 6
varXCopy = quote do:
var `x` = 7
echoX = nnkCall.newTree(ident"echo",x)
blockX = newBlockStmt(
newStmtList(varX,echoX)) #echo 5
blockXNoCopy = newBlockStmt(
newStmtList(varXNoCopy,echoX)) #echo 5
blockXCopy = newBlockStmt(
newStmtList(varXCopy,echoX.copy())) #echo 7
result = newStmtList(blockX,blockXNoCopy,blockXCopy)
echo result[0][1][1][1] == result[1][1][1][1] #true
echo result[0][1][1][1] == result[2][1][1][1] #true
echo repr result
when false: #result
block:
var test = 5
echo(test) # test = 5
#result[0][1][1][1]
block:
var test = 6
echo(test) # test = 5
#result[1][1][1][1]
block:
var test = 7
echo(test) # test = 7
#result[2][1][1][1]
tester()
Is there a function like sameNode(a,b)?
If not it should be added provided the implementation is easy enough.
sameNode(result[0][1][1][1],result[1][1][1][1]) == true
sameNode(result[0][1][1][1],result[2][1][1][1]) == false