> 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