Re: Help getting this macro to work in a for loop

2019-06-15 Thread kaushalmodi
Thank you! That was a very good explanation; it now helps me understand why 
macros cannot work in for loops.


Re: Help getting this macro to work in a for loop

2019-06-15 Thread kaushalmodi
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.


Re: Help getting this macro to work in a for loop

2019-06-15 Thread solo989
try this: 


import macros

proc replaceSymAndIdent(a : NimNode, b : NimNode, c : NimNode, isLit : 
static[bool] = true) =
for i in 0..len(a)-1:
  if a[i].kind == nnkSym:
if ident(strVal(a[i])) == b:
  a[i] = c
  elif a[i].kind == nnkClosedSymChoice or a[i].kind == nnkOpenSymChoice:
if ident(strVal(a[i][^1])) == b:
  a[i] = c
  elif a[i].kind == nnkIdent:
if a[i] == b:
  a[i] = c
  elif a[i].len != 0:
if a[i].kind == nnkDotExpr:
  when not isLit:
if a[i][0].kind == nnkSym:
  if ident(strVal(a[i][0])) == b:
a[i][0] = c
elif a[i][0].kind == nnkClosedSymChoice or a[i][0].kind == 
nnkOpenSymChoice:
  if ident(strVal(a[i][0][^1])) == b:
a[i][0] = c
elif a[i][0].kind == nnkIdent:
  if a[i][0] == b:
a[i][0] = c
else:
  replaceSymAndIdent(a[i], b, c, isLit)

macro iterateSeq(a : untyped, b : static[seq[string]], code : untyped) : 
untyped =
  let a =
if a.kind == nnkSym:
  ident($a)
elif a.kind == nnkClosedSymChoice or a.kind == nnkOpenSymChoice:
  ident($a[^1])
else:
  a
  #echo type(a)
  result = newStmtList()
  for item in b:
var newCode = code.copy()
replaceSymAndIdent(newCode, a, newLit(item), isLit = true)
result.add(newCode)
  #echo treeRepr result
  #echo repr result
  #echo ""
  #result = newStmtList()

macro assignField*(obj, fieldName, value: typed) : untyped =
  let fieldSym = newIdentNode($`fieldName`)
  result = quote do:
`obj`.`fieldSym`=`value`

type
  SomeObj* = object
a* : int
b* : int
c* : int

var
  foo = SomeObj()

iterateSeq(field, @["a", "c"]):
  assignField(foo, field, 50)

echo foo

assignField(foo, "a", 100)
assignField(foo, "c", 200)

block:
  assignField(foo, "b", 300)

echo foo


Run

Also, I noticed you were missing a return type on your macro so I don't know 
how your code compiled at all.


Re: Help getting this macro to work in a for loop

2019-06-14 Thread jyapayne
This is not possible with a macro. Macros are limited to compile time only, and 
what you want to do is access the fields at run time.

Here is the code inline for reference:


import macros

macro assignField*(obj, fieldName, value: typed) =
  let fieldSym = newIdentNode($`fieldName`)
  result = quote do:
`obj`.`fieldSym`=`value`

type
  SomeObj* = object
a*: int
b*: int
c*: int

var
  foo = SomeObj()

for field in @["a", "c"]:
  # this doesn't work
  assignField(foo, field, 50)
# /usercode/in.nim(19, 14) template/generic instantiation of `assignField` 
from here
# /usercode/in.nim(5, 12) Error: attempting to call undeclared routine: 
'field='

# this works
assignField(foo, "a", 100)
assignField(foo, "c", 200)

block:
  # this works
  assignField(foo, "b", 300)


echo foo


Run

In `assignField(foo, field, 50)` above in the for loop, the compiler can only 
infer the name you passed directly to the macro, which is `field`. So what the 
compiler is doing is trying to call `foo.field = 50`, but field doesn't exist.

Since you are trying to use a seq to loop through (`for field in @["a", 
"c"]:`), the compiler can't use that information since at run time a seq may 
change. In theory, you could use a constant array, but I was unable to get it 
to work since it seems that for loops always execute at runtime from the 
compiler's point of view.


Re: Help getting this macro to work in a for loop

2019-06-14 Thread jxy
Macros expand at compile time, while for loops execute at run time.

You need to loop it inside a macro.


Help getting this macro to work in a for loop

2019-06-14 Thread kaushalmodi
Hello,

ftsf on IRC and I were trying to get a macro work that helps assign value to a 
Nim object field where the field is specified by a string value.

Discussion: 
[https://irclogs.nim-lang.org/14-06-2019.html#17:39:56](https://irclogs.nim-lang.org/14-06-2019.html#17:39:56)

We almost have a solution: 
[https://play.nim-lang.org/index.html?ix=1LMK](https://play.nim-lang.org/index.html?ix=1LMK)

How can that be fixed?

Thanks!