ch33.md 2.7 KB

Chapter 33. AST introspection

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.