bindSym

The above debug macro relies on the fact that write, writeLine and stdout are declared in the system module and are thus visible in the instantiating context. There is a way to use bound identifiers (aka symbols) instead of using unbound identifiers. The bindSym builtin can be used for that:

  1. import std/macros
  2. macro debug(n: varargs[typed]): untyped =
  3. result = newNimNode(nnkStmtList, n)
  4. for x in n:
  5. # we can bind symbols in scope via 'bindSym':
  6. add(result, newCall(bindSym"write", bindSym"stdout", toStrLit(x)))
  7. add(result, newCall(bindSym"write", bindSym"stdout", newStrLitNode(": ")))
  8. add(result, newCall(bindSym"writeLine", bindSym"stdout", x))
  9. var
  10. a: array[0..10, int]
  11. x = "some string"
  12. a[0] = 42
  13. a[1] = 45
  14. debug(a[0], a[1], x)

The macro call expands to:

  1. write(stdout, "a[0]")
  2. write(stdout, ": ")
  3. writeLine(stdout, a[0])
  4. write(stdout, "a[1]")
  5. write(stdout, ": ")
  6. writeLine(stdout, a[1])
  7. write(stdout, "x")
  8. write(stdout, ": ")
  9. writeLine(stdout, x)

In this version of debug, the symbols write, writeLine and stdout are already bound and are not looked up again. As the example shows, bindSym does work with overloaded symbols implicitly.

Note that the symbol names passed to bindSym have to be constant. The experimental feature dynamicBindSym (experimental manual) allows this value to be computed dynamically.