metaprogrammingnim-langrepresentation

How can I see the contents of a NimNode?


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.?


Solution

  • 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)))