Re: Iterate over fields

2020-04-20 Thread Clonk
I think i understand most of it. Now, I just need to practice macros more so I 
can come up with the solution my self next time :).

Thanks again


Re: Iterate over fields

2020-04-17 Thread Vindaar
First of all see of course the docs here:

[https://nim-lang.github.io/Nim/macros.html#quote%2Ctyped%2Cstring](https://nim-lang.github.io/Nim/macros.html#quote%2Ctyped%2Cstring)

and the macro tutorial, specifically:

[https://nim-lang.github.io/Nim/tut3.html#introduction-generating-code](https://nim-lang.github.io/Nim/tut3.html#introduction-generating-code)

So the basic idea is that quote do allows you to write exactly the code you 
want to generate. However, in most cases that's not really very helpful, 
because if you can explicitly write your code, you could also just write a 
template / proc. That's where the back ticks come in to perform actual quoting 
of NimNodes defined in the current scope. They will be inserted in those places.

quote do is thus just a nice way to avoid having to build the AST manually (as 
I for instance do in the newVar proc), but keep the ability to insert NimNodes 
you calculate / determine somehow based on what the macro is supposed to 
accomplish.

Another thing to keep in mind when using quote do is about the stuff that's not 
quoted with back ticks. As a rule of thumb (someone please correct me):

  * any procedure / template you use within quote do will be bound in the scope 
where the macro code is injected
  * any variables you introduce will be "gensym'd", that is for each symbol you 
introduce a unique symbol will be created. So if you write var x = 5 within 
quote do, the final code won't have the variable x, but something like x_12345.



The second means that if you want to refer to some variable that will be known 
in the scope in which the macro is used, you have to create the identifier 
manually and quote it. Due to the first point you fortunately don't have to do 
the same for procedures you want to use.


Re: Iterate over fields

2020-04-17 Thread b3liever
There is also 
[json.to](https://github.com/nim-lang/Nim/blob/devel/lib/pure/json.nim#L1201) 
as an example.


Re: Iterate over fields

2020-04-17 Thread Clonk
I didn't about the quote do tricks, that's useful !

Why do you have to use backtick "`" inside quote do ? Is it to interpret macro 
inside a block of code ?


Re: Iterate over fields

2020-04-16 Thread Vindaar
> I'll look into your solution since I may need to adapt a few things (I've 
> simplified the real uses cases to summarize it into a single problems). The 
> goal is also to learn Nim's macro as well. I've now spent probably as much 
> time on macros than it would have took to write the solution it by hand, but 
> it's not as fun.

If you have questions about my code there or general macro questions, just ask. 
I'll try to help!


Re: Iterate over fields

2020-04-16 Thread Clonk
> If all of your procs are going to look like newFooBar above there, it's 
> possible to generate with a macro.

Yes, it's exactly what I'm looking for. A way to generate several procedure 
that follow a pattern.

I've been trying my hand at writing macros that generate proc but there is a 
lot to unpack regarding the Nim macro system, it's not as intuitive as the rest 
of the language :).

I'll look into your solution since I may need to adapt a few things (I've 
simplified the real uses cases to summarize it into a single problems).

Thanks everyone for the help ! 


Re: Iterate over fields

2020-04-16 Thread Vindaar
If all of your procs are going to look like newFooBar above there, it's 
possible to generate with a macro.


import macros, tables

type
  Tensor[T] = object
discard
  
  Model = object
field1: string
field2: string
field3: int
  
  FooObj = object
field1: Tensor[float]
field2: Table[string, float]
field3: int

proc unpack[T; U](arg: T, to: var U) = discard

proc newVar(name, dtype: NimNode): NimNode =
  result = nnkVarSection.newTree(
nnkIdentDefs.newTree(
  ident(name.toStrLit.strVal), # replace by new ident
  dtype,
  newEmptyNode()
)
  )
  echo result.repr

macro genNewObjProc(obj, model: typed): untyped =
  let objFields = obj.getType[1].getTypeImpl[2] # get recList of type
  let modelFields = obj.getType[1].getTypeImpl[2] # get recList of type
  doAssert objFields.len == modelFields.len
  var body = newStmtList()
  let modelIdent = ident"model"
  # variable to hold object constructor `FooBar(field1: field1,...)`
  var objConstr = nnkObjConstr.newTree(obj)
  for i in 0 ..< objFields.len:
let modelName = ident(modelFields[i][0].toStrLit.strVal) # replace by 
new ident
doAssert eqIdent(objFields[i][0], modelName)
let objType = objFields[i][1]
body.add newVar(modelName, objType)
body.add quote do:
  unpack(`modelIdent`.`modelName`, `modelName`)
# add to object constructor
objConstr.add nnkExprColonExpr.newTree(modelName, modelName)
  
  # add resulting `FooObj` call
  let resIdent = ident"result"
  body.add quote do:
`resIdent` = `objConstr`
  
  let procParams = [obj, # return type
nnkIdentDefs.newTree(modelIdent,
 model,
 newEmptyNode())]
  result = newProc(name = ident("new" & obj.toStrLit.strVal),
   params = procParams,
   body = body)
  echo result.repr

genNewObjProc(FooObj, Model)


Run

I'm not sure how helpful it is to get such a macro if one isn't familiar with 
macros. But since it's fun to write I might as well give you a solution. :)


Re: Iterate over fields

2020-04-15 Thread treeform
Field fieldPairs are great!

I use it to print arbitrary structures and deserialize json in a more looser 
way:

[https://github.com/treeform/print/blob/master/src/print.nim#L79](https://github.com/treeform/print/blob/master/src/print.nim#L79)

[https://github.com/treeform/jsutils/blob/master/src/jsutils/jsons.nim#L28](https://github.com/treeform/jsutils/blob/master/src/jsutils/jsons.nim#L28)


Re: Iterate over fields

2020-04-15 Thread Stefan_Salewski
I have never used that iterators myself.

But I would strongly assume that we can only iterate over one object at a time.

As we can only iterate over one string or over one sequence at a time.

But of course we can collect the results of each iteration is some way, and 
finally output all collected data in the desired format. 


Re: Iterate over fields

2020-04-15 Thread Clonk
I thought fields could only iterate over element of the same type. If not, how 
do you use fields to iterate over fields of 2 different type ?


type
  FooObj = object
field1: Tensor[float]
field2: Table[string, float]
field3: int
  Model = object
field1: string
field2: string
field3: int

var model: Model
var foo: FooObj

for name, t, m in fieldPairs(model, foo):
   echo name, t, $m


Run

I get an error `type mismatch: got  but expected 'Model = object`


Re: Iterate over fields

2020-04-14 Thread Stefan_Salewski
There are iterators fields and fieldPairs:

[https://nim-lang.org/docs/iterators.html#fieldPairs.i%2CT](https://nim-lang.org/docs/iterators.html#fieldPairs.i%2CT)


Iterate over fields

2020-04-14 Thread Clonk
Hello,

I'm working on some serialization / deserialization using msgpack and I was 
wondering if there was a way to use the macros system to iterate over field.

To illustrate here's what i'm ttrying to do :


type
  Model = object
field1: string
field2: string
field3: int
  
  FooObj = object
field1: Tensor[float]
field2: Table[string, float]
field3: int

proc newFooObj(model: Model): FooObj=
  var field1: Tensor[float]
  unpack(model.field1, field1)
  var field2: Table[string, float]
  unpack(model.field2, field2)
  var field3: int
  unpack(model.field3, field3)
  
  result = FooOBj(field1: field1, field2: field2, field3: field3)
  

Run

Note that the name of the corresponding fields between type will always be 
identical.

I have a dozen of type (for each serailized / deserialzed type) and each type 
has lots of fields so writing by hand is doable by very tedious.

I'm not familiar with the macro system but I was wondering if there was a way 
to iterate over the fields of an object with or without a bracket type (I've 
used dumpTree and noticed a difference) and apply so I could simply pass 2 type 
with identical field name and generate a procedure:


type
  Model = object
field1: string
field2: string
field3: int
  
  FooObj = object
field1: Tensor[float]
field2: Table[string, float]
field3: int

magicalProcGeneratingMacro[Model, FooObj](procName)


Run

I'm not experienced enough with Nim's macro system to see the solution (if 
there is any) on my own or if I should just give up and do it by hand.