How can I see the contents of a NimNode?

80 Views Asked by At

I'm using the nim programming language and am doing some metaprogramming.

I want to write a DSL of some sorts that enables generating procs. For that I want to pass some nim-code into a macro (e.g. a proc definition) and eventually generate the proc from that.

Now I know you can capture nim-code inside of a macro with syntax such as this:

macro generateMapper(body: untyped): untyped =
  echo body.repr


generateMapper():
  proc useThisToGenerateAProc(source: A, target: B)

Which puts proc useThisToGenerateAProc(source: A, target: B) into body, but echo'ing body.repr doesn't show anything.

How can I see what NimNodes are actually inside body or other NimNodes in general etc.?

1

There are 1 best solutions below

0
Philipp Doerner On BEST ANSWER

You'll want to use these procs from std/macros (in order of most verbose representation to least):

These will print you a representation at compile-time of the NimNode and everything it contains. AstGenRepr of those 3 is the one that is closest to the code you'd actually write inside a macro where you deal with the Nodes yourself.

An example with astGenRepr:

macro generateMapper(body: untyped): untyped =
  echo body.astGenRepr

generateMapper():
  proc myMapProc(source: A, target: B): string

This prints (With comments from myself):

nnkStmtList.newTree(           # The general "box" representing the entire line of code
  nnkProcDef.newTree(          # The general "box" representing the entire proc definition
    newIdentNode("myMapProc"), # The name of the proc
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree(   # The general "box" representing all parameters of this proc
      newIdentNode("string"),  # The return type, in this case string. Can be newEmptyNode() in case of no return type
      nnkIdentDefs.newTree(    # The general "box" representing the first parameter
        newIdentNode("source"),# Name of the first parameter
        newIdentNode("A"),     # Type of the first parameter
        newEmptyNode()
      ),
      nnkIdentDefs.newTree(    # The general "box" representing the second parameter
        newIdentNode("target"),# Name of the second parameter
        newIdentNode("B"),     # Type of the second parameter
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    newEmptyNode()
  )
)

Compared to that the more compact treeRepr:

StmtList
  ProcDef
    Ident "myMapProc"
    Empty
    Empty
    FormalParams
      Ident "string"
      IdentDefs
        Ident "source"
        Ident "A"
        Empty
      IdentDefs
        Ident "target"
        Ident "B"
        Empty
    Empty
    Empty
    Empty

And the even more compact lisprepr:

(StmtList (ProcDef (Ident "myMapProc") (Empty) (Empty) (FormalParams (Ident "string") (IdentDefs (Ident "source") (Ident "A") (Empty)) (IdentDefs (Ident "target") (Ident "B") (Empty))) (Empty) (Empty) (Empty)))