# Chapter 35. Collect macro As our first complex example we will look at how Nim’s collect macro can be implemented. The standard library already contains collect, it can be found in std/sugar. collect is the preferred method of turning a potentially nested loop construct from a statement to an expression. Instead of: import std / tables const Data = toTable({"a": 1, "b": 2, "c": 3}) var s = newSeq[string]() for k, v in Data.pairs: if v mod 2 == 0: s.add k One can use the more declarative: import std / [tables, sugar] const Data = toTable({"a": 1, "b": 2, "c": 3}) let s = collect(newSeq): for k, v in Data.pairs: if v mod 2 == 0: k An an exercise we will reimplement collect. For a beginner, writing a macro is usually a hard task. As the first step we postulate the code pattern that the macro needs to expand to: collect(constructorCall): body should be translated into something like: 235 block: var tmp = constructorCall[typeOf(body)]() sinkInto(body, tmp.add) tmp where sinkInto(body, tmp.add) describes the AST where the final expression x of body is replaced by tmp.add x. We have to walk if-expressions, loops and “statement list expressions” to arrive at the “final expression” which is the part of the body that produces the value: import macros proc sinkInto(n, fullBody, res, bracketExpr: NimNode): NimNode = 1 case n.kind of nnkStmtList, nnkStmtListExpr, nnkBlockStmt, nnkBlockExpr, nnkWhileStmt, nnkForStmt, nnkElifBranch, nnkElse, nnkElifExpr, nnkOfBranch, nnkExceptBranch: 2 result = copyNimTree(n) if n.len >= 1: result[^1] = sinkInto(n[^1], fullBody, res, bracketExpr) of nnkIfExpr, nnkIfStmt, nnkTryStmt: 3 result = copyNimTree(n) for i in 0..