@Udiknedormin

Sorry, you are absolutely right! Thank you.

2018-04-16 14:05:54

@Udiknedormin

The breeze is still a macro which build AST.

We know a macro can turn code into AST, is there any way to turn AST into code and I can call the code in a macro.

2018-04-16 14:37:18

@slangmgh

Well, most of macros build AST, I'd say. What you really mean, I think, is: breeze generates code which generates AST. It's a subtle difference which is connected to your question, actually.

  • breeze: code() -> code(() -> AST)
  • macro you use breeze in: code(() -> AST) --AST--> code

As for your question: well, it's a little tricky. Templates and macros do code->code. Proc do AST->AST. getAst makes macros behave like procs here. The general problem is: how do you want to have AST -> code when it means the caller modifies itself in runtime?

There is a solution however. The caller can't modify itself, so no AST -> code is possible. But macros operate on AST or static[...] in the meantime. So code --AST--> code --static[...]--> code is possible. Then, you can split your macros in two or more steps. Each of them can return some code (from AST) and the call to the next macro step. Like this:

import macros


macro stepTwo(input: static[int]): untyped =
  echo "In step 2:  "
  echo "wow! normal int!  ", input
  echo ""
  result = (2*input).newLit

macro stepOne(fun, input: typed): untyped =
  echo "In step 1:"
  echo fun.treeRepr
  echo input.treeRepr
  echo ""
  result = newCall("stepTwo", newCall(fun, input))

proc incrementor(by: int): proc(x: int): int =
  proc inner(x: int): int =
    x + by
  inner

const x = 3
static:
  echo "Outside: ", stepOne(incrementor(2), x)

Here you have it. The integer value that stepTwo operated on was actually calculated using AST generated by macro, so you really turned AST into code while still working with macros. Please note you can move all the NimNodes you operated on through AST->code->AST (AST->code at macro return, code->AST at another macro call). You can even typecheck them if you want to (see: typed arguments). I think it might be the only way to get typed nodes from untyped ones, actually.

Sadly, no generic for static[...] is possible in macros. Oh well. Could have been worse.

To tell you the truth, I was thinking about a macro utilities library (with hope for merging with core/macros ) and it's quite possible I'll include a macro to do what I've just shown you, so that you could do:

macro stepOne(fun, input: typed): untyped {.withAstExec.} =
  let calc = newCall(fun, input)
  let num = execAst[int](calc)
  result = (2*num).newLit

So now you tell me whenever using macros inside macros sounds feasible or not? And now maybe a one-liner for that:

macro stepOne(fun, input: typed): untyped {.withAstExec.} =
  newLit(2 * execQuote[int]( `fun`(`input`) ))

2018-04-16 21:29:59

@Udiknedormin

It's just like magic!!!

I still need time to understand it.

2018-04-17 00:46:25

@Udiknedormin

Sorry, silly of me, I still cannot figure out how to make the following code possible:

let calc = newCall(fun, input)
let num = execAst[int](calc)    ***** howto *****

The code shows in the stepTwo, we get the result of the function, but how to return this value to stepOne?

2018-04-17 13:28:44

@slangmgh

That's why I added withAstExec pragma. It should split your macro into as many steps as needed, each with all local variables of the previous one with additional argument of static[T] where T is the type you provided explicitly (int here).

2018-04-17 15:33:56
<<<••12••>>>