Thank you!

I am a real beginner in Nim macros. Your example snippet will serve really 
helpful to me as I am learning this more.

\---

I'd also like to thank @JasperJenkins (from Gitter) for offering his solution 
to this problem on IRC here: 
[https://irclogs.nim-lang.org/14-06-2019.html#18:34:24](https://irclogs.nim-lang.org/14-06-2019.html#18:34:24)

Here's the Nim playground link he shared: 
[https://play.nim-lang.org/index.html?ix=1LMY](https://play.nim-lang.org/index.html?ix=1LMY)

For convenience, that solution is inlined below:
    
    
    import macros
    {.experimental: "forLoopMacros".}
    
    type
      Obj = object
        a, b, c: int
    
    proc replace(n: NimNode, symbolic, real: NimNode): NimNode =
      if n.kind == nnkIdent and n.eqIdent(symbolic):
        # this identifier is the for loop variable: return a real field ident 
instead
        return real
      else:
        result = n # return unchanged verbatim
        # continure recursion on children
        for i in 0 ..< n.len:
          n[i] = replace(n[i], symbolic, real)
    
    macro replaceField(forLoop: ForLoopStmt): untyped =
      echo "\nCode before:\n", toStrLit(forLoop).strVal
      result = newTree(nnkStmtList)
      let
        forVar = forLoop[0] # k
        fields = forLoop[1][1] # ["a", "b", "c"]
        stmts = forLoop[2] # loop body
      for f in fields:
        # Copy the loop body since the loop gets unrolled.
        # We need a block so any variables don't collide.
        let stmtsCopy = newBlockStmt(copyNimTree(stmts))
        # recursively replace the forVar with a new identifer.
        result.add(replace(
          stmtsCopy, # this fields copy of the stmts
          forVar, # identifier to replace
          ident(f.strVal))) # new identifer made from one of the real obj fields
      echo "\nCode after:", toStrLit(result).strVal, "\n"
    
    var obj = Obj(a: 1, b: 2, c: 3)
    echo obj
    
    for k in replaceField(["a", "b", "c"]):
      # more elaborate example that would fail without a block statement
      let num = obj.k
      for i in 1 .. num:
        obj.k *= i
    
    echo obj
    
    
    Run

\---

This is awesome! I now have more than one solutions to understand more complex 
macros than the `quote do` ones.

Reply via email to