Debug Example

The following example implements a powerful debug command that accepts a variable number of arguments:

  1. # to work with Nim syntax trees, we need an API that is defined in the
  2. # ``macros`` module:
  3. import macros
  4. macro debug(args: varargs[untyped]): untyped =
  5. # `args` is a collection of `NimNode` values that each contain the
  6. # AST for an argument of the macro. A macro always has to
  7. # return a `NimNode`. A node of kind `nnkStmtList` is suitable for
  8. # this use case.
  9. result = nnkStmtList.newTree()
  10. # iterate over any argument that is passed to this macro:
  11. for n in args:
  12. # add a call to the statement list that writes the expression;
  13. # `toStrLit` converts an AST to its string representation:
  14. result.add newCall("write", newIdentNode("stdout"), newLit(n.repr))
  15. # add a call to the statement list that writes ": "
  16. result.add newCall("write", newIdentNode("stdout"), newLit(": "))
  17. # add a call to the statement list that writes the expressions value:
  18. result.add newCall("writeLine", newIdentNode("stdout"), n)
  19. var
  20. a: array[0..10, int]
  21. x = "some string"
  22. a[0] = 42
  23. a[1] = 45
  24. 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)

Arguments that are passed to a varargs parameter are wrapped in an array constructor expression. This is why debug iterates over all of n’s children.