For loop macro

A macro that takes as its only input parameter an expression of the special type system.ForLoopStmt can rewrite the entirety of a for loop:

  1. import macros
  2. macro enumerate(x: ForLoopStmt): untyped =
  3. expectKind x, nnkForStmt
  4. # check if the starting count is specified:
  5. var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1]
  6. result = newStmtList()
  7. # we strip off the first for loop variable and use it as an integer counter:
  8. result.add newVarStmt(x[0], countStart)
  9. var body = x[^1]
  10. if body.kind != nnkStmtList:
  11. body = newTree(nnkStmtList, body)
  12. body.add newCall(bindSym"inc", x[0])
  13. var newFor = newTree(nnkForStmt)
  14. for i in 1..x.len-3:
  15. newFor.add x[i]
  16. # transform enumerate(X) to 'X'
  17. newFor.add x[^2][^1]
  18. newFor.add body
  19. result.add newFor
  20. # now wrap the whole macro in a block to create a new scope
  21. result = quote do:
  22. block: `result`
  23. for a, b in enumerate(items([1, 2, 3])):
  24. echo a, " ", b
  25. # without wrapping the macro in a block, we'd need to choose different
  26. # names for `a` and `b` here to avoid redefinition errors
  27. for a, b in enumerate(10, [1, 2, 3, 5]):
  28. echo a, " ", b