> BTW, could you please post an example code about "quote" ?

Well, I'll post an example of nested `with`, with a sample `quote` use, and the 
corresponding manual construction.

The example uses a loop for dots handling - you can still write a recursive 
version yourself.

Yet you may want to add support for indexed properties (`a.b[c].d`), yet other 
statements kinds besides assignment: handle other `prop.kind` and `i.kind` 
cases; enough scope for experiments.

I'd say this all example is rather educational and practically an overkill - 
for this particular case that first strings concatenation approach seems to be 
more appropriate.
    
    
    type Obj = object
      num: int
      obj: ref Obj
      str: string
    var
      anObject: Obj
    
    import macros
    
    # this is not for dots: for not having to write ``new anObject.obj``, ...
    proc initRefObjectIfNeeded(ob: NimNode): NimNode =
      quote do:
        when compiles(isNil(`ob`)):
          if isNil(`ob`): new `ob`
    
    macro with(obj : typed, body : untyped) : untyped  =
        result = newStmtList()
        for i in body :
            if i.kind == nnkAsgn :
              #echo "i:\n", i.treeRepr
              var prop = i[0] # everything to the left of ``=``
              var dots = newSeq[NimNode]() # here we'll gather dot-delimited 
parts
              while true:
                case prop.kind
                of nnkIdent: # the final part, end of "recursion", breaking here
                  dots.add prop
                  break
                of nnkDotExpr:
                  #echo "prop:\n", prop.treeRepr
                  dots.add prop[1] # only one part is here
                  prop = prop[0]   # all other parts are here, we'll continue 
with them
                else:
                  error($prop.kind & "not supported")
              var lhs = newDotExpr(obj, nil) # we'll replace ``nil`` later
              #for j in dots: echo j.treeRepr # what we've gathered
              #echo "-"
              for j in countdown(dots.high, 1): # we have them in reverse order
                lhs[0] = newDotExpr(lhs[0], dots[j])
                result.add initRefObjectIfNeeded(lhs[0]) # not to write ``new 
...``
              lhs[1] = dots[0] # final step: replacing that ``nil`` with the 
right-most part
              #echo "lhs:\n", lhs.treeRepr
              let rhs = i[1] # the value being assigned
              result.add quote do:
                `lhs` = `rhs`
              # the same without quote:
              #result.add newAssignment(lhs, rhs)
        #echo "result:\n", result.treeRepr
        #echo result.repr
    
    # we would have need this w/o ``initRefObjectIfNeeded``
    #new anObject.obj
    #new anObject.obj.obj
    #new anObject.obj.obj.obj
    
    with anObject:
      num = 42
      obj.str = "Pear"
      obj.obj.str = "Banana"
      obj.obj.obj.str = "YetSomeFruit"
    
    echo anObject.repr
    
    
    Run

Reply via email to