With macros you can start by observing the AST generated with `dumpTree` or 
`dumpAstGen`.

So start with :
    
    
    import std/[macros]
    dumpTree:
      
      type
         Person* = object
           name*: string
           job*: string
           email*: string
    
    
    
    Run

This will output the following :
    
    
    StmtList
      TypeSection
        TypeDef
          Postfix
            Ident "*"
            Ident "Person"
          Empty
          ObjectTy
            Empty
            Empty
            RecList
              IdentDefs
                Postfix
                  Ident "*"
                  Ident "name"
                Ident "string"
                Empty
              IdentDefs
                Postfix
                  Ident "*"
                  Ident "job"
                Ident "string"
                Empty
              IdentDefs
                Postfix
                  Ident "*"
                  Ident "email"
                Ident "string"
                Empty
    
    
    
    Run

This gives you the AST in a tree readable format. Now do the same with 
`dumpAstGen` this gives you the output in "macro compatible" format :
    
    
    nnkStmtList.newTree(
      nnkTypeSection.newTree(
        nnkTypeDef.newTree(
          nnkPostfix.newTree(
            newIdentNode("*"),
            newIdentNode("Person")
          ),
          newEmptyNode(),
          nnkObjectTy.newTree(
            newEmptyNode(),
            newEmptyNode(),
            nnkRecList.newTree(
              nnkIdentDefs.newTree(
                nnkPostfix.newTree(
                  newIdentNode("*"),
                  newIdentNode("name")
                ),
                newIdentNode("string"),
                newEmptyNode()
              ),
              nnkIdentDefs.newTree(
                nnkPostfix.newTree(
                  newIdentNode("*"),
                  newIdentNode("job")
                ),
                newIdentNode("string"),
                newEmptyNode()
              ),
              nnkIdentDefs.newTree(
                nnkPostfix.newTree(
                  newIdentNode("*"),
                  newIdentNode("email")
                ),
                newIdentNode("string"),
                newEmptyNode()
              )
            )
          )
        )
      )
    )
    
    
    
    Run

This is the output.

Now use that out to construct your macro ; mostly we need to change the object 
type ident name and the fields.

What I will do is construct the `nnkRecList.newTree` separetely because I want 
to iterate to build it and then replace the name of the type by the argument in 
`newIdentNode("Person")` .

This is the outcome :
    
    
    import std/[macros]
    
    dumpAstGen:
      type
         Person* = object
           name*: string
           job*: string
           email*: string
    
    macro buildType(typName: untyped, fieldNames: varargs[untyped]) : untyped =
      # First construct the AST based on varargs
      var fieldsAst = nnkRecList.newTree()
      for f in fieldNames:
        fieldsAst.add nnkIdentDefs.newTree(
          # postfix is the * in the field
          nnkPostFix.newTree(
            newIDentNode("*"),
            newIdentNode(strVal(f))
          ),
          # This assume all the fields are string
          newIdentNode("string"),
          newEmptyNode()
        )
      
      result = nnkStmtList.newTree(
        nnkTypeSection.newTree(
          nnkTypeDef.newTree(
            nnkPostfix.newTree(
              newIdentNode("*"),
              newIdentNode(strVal(typName))
            ),
            newEmptyNode(),
            nnkObjectTy.newTree(
              newEmptyNode(),
              newEmptyNode(),
              # Replace the specific AST with a generic one :
              fieldsAst
            )
          )
        )
      )
      echo result.repr
    
    # Check it works
    buildType(Person, name, job, email)
    var alice = Person(name: "Alice", job: "Dev", email: "f...@bar.com")
    echo alice
    
    buildType(Animal, name, species, toy)
    var bob = Animal(name: "Bob", species: "dog", toy: "ball")
    echo bob
    
    
    
    Run

Reply via email to