The mapping from Nim’s syntax to syntax trees is rather subtle. While the syntax is optimized for readability and conciseness the syntax trees are designed for ease of construction and traversal. Like in Lisp the tree consists of nested nodes where each node is of a certain "kind" such as "if statement" (nnkIfStmt) or "routine call" (nnkCall). The mapping can easily be seen with treeRepr: import std / macros macro investigate(body: untyped) = 1
echo treeRepr body 2
investigate:
if undeclaredIdentifier == 3:
echo "3"
else:
echo "not 3"
1 Declares a macro called investigate that works on untyped trees. 2 Calls treeRepr which produces a debug string of body. Because macro expansion happens at compile time this program produces at compile time:
229
StmtList
IfStmt
ElifBranch
Infix
Ident "=="
Ident "undeclaredIdentifier"
IntLit 3
StmtList
Command
Ident "echo"
StrLit "3"
Else
StmtList
Command
Ident "echo"
StrLit "not 3"
We can see that a list of statements StmtList is passed to investigate. In order to create a StmtList one can use newTree(nnkStmtList, ). 33.1. Typed vs untyped ASTs The difference between typed and unytyped parameters is important for templates and it is even more important for macros. The AST that is passed to a typed macro parameter can differ significantly from an AST that is passed to an untyped macro parameter. For example: import std / macros macro investigateTyped(body: typed) =
echo treeRepr body
var needsToBeDeclaredIdentifier = 0 investigateTyped:
if needsToBeDeclaredIdentifier == 3:
echo "3"
else:
echo "not 3"
This program produces at compile time: 230 StmtList
IfStmt
ElifBranch
Infix
Sym "=="
Sym "needsToBeDeclaredIdentifier"
IntLit 3
Command
Sym "echo"
HiddenStdConv
Empty
Bracket
StrLit "3"
Else
Command
Sym "echo"
HiddenStdConv
Empty
Bracket
StrLit "not 3"
Note how symbol lookups happened producing nnkSym nodes and the echo calls have mysterious hidden conversion nodes containing an nnkBracket node. In other words, echo "3" was transformed into echo ["3"] because echo uses a varargs parameter. Many details like these have to be understood before one can write a macro operating on typed ASTs. For this reason most of the following examples operate on untyped ASTs.