Is there a way to get unmodified / uninterpreted source code (as either string or AST) of an expression in a macro?
motivation 1: to allow writing logging functions that show exact expression on left hand side, eg: mylog(1+1) # prints 1+1=2 motivation 2: to allow writing smart assert function that shows values of expression in assert when it failed, eg: myassert(1+1==3) #errors with: assert failed: 1+1(=2) == 3(3)
in Nim I wasn't able to make it work with toStrLit as it seems to do const-folding before it's being passed as AST, so my implementations of mylog and myassert only work well with runtime expressions that aren't const-folded:
macro mylog(x:typed): typed =
result = newNimNode(nnkStmtList, x)
result.add(newCall("write", newIdentNode("stdout"), toStrLit(x)))
result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(":")))
result.add(newCall("writeLine", newIdentNode("stdout"), x))
var x=1
mylog(x+1) # x + 1:2 (OK but I'd rather want exact source code with spacing eg: `x+1:2`)
mylog(1+1) # 2:2 (not OK, I want: 1+1: 2)
NOTE: In D I had a PR that achieved exactly that: https://github.com/dlang/dmd/pull/7821 ; you can see further motivations for this feature there.
I'd be looking for some (magic) function like this:
toSourceLit(x) that would return the (slice) of the source code that was parsed as expression x.
NOTE: this would require storing source code in memory; I don't know if Nim already keeps in memory source code for parsed files but FWIW in that PR this additional memory requirement was negligible, see https://github.com/dlang/dmd/pull/7821#issue-166411298
Perhaps this is what you want, dump https://nim-lang.org/docs/future.html#dump.m,typed
Also look this tutorial to achieve what you want, https://nim-lang.org/docs/tut2.html#macros-expression-macros
As for literally with spacing, macro got its argument as NimNode and there's no nnkWhitespace.
Perhaps this is what you want, dump https://nim-lang.org/docs/future.html#dump.m,typed
dump does not work at all and in fact the documentation is misleading: as it would appear in source code is not true: dump(1+1) prints 2 = 2 instead of 1 + 1 = 2 which is exactly the issue I'm trying to address in this thread. Also even if it did work, I'd like to get a string (eg for further processing) instead of dumping to stdout which is less flexible
Also look this tutorial to achieve what you want, https://nim-lang.org/docs/tut2.html#macros-expression-macros
I know about that, but this also uses toStrLit(x) and has the exact same issue.
So looks like Nim doesn't support what I'm looking for. Could this be added? It's a useful addition for debugging / logging.
It works when the macro parameter is untyped, I guess constant folding happens during the typing process:
import macros
macro mylog(x:untyped): untyped =
result = newNimNode(nnkStmtList, x)
result.add(newCall("write", newIdentNode("stdout"), toStrLit(x)))
result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(":")))
result.add(newCall("writeLine", newIdentNode("stdout"), x))
var x=1
mylog(x+1) # x + 1:2 (OK but I'd rather want exact source code with spacing eg: `x+1:2`)
mylog(1+1) # 2:2 (not OK, I want: 1+1: 2)
This is from tutorial
import macros
macro debug(x: varargs[untyped]): typed =
result = newStmtList()
for s in x:
var slit = s.toStrLit
var stringExpr = slit.repr
echo stringExpr & " okay"
result.add newCall("write", newIdentNode("stdout"), slit)
result.add newCall("write", newIdentNode("stdout"), newStrLitNode(": "))
result.add newCall("writeLine", newIdentNode("stdout"), s)
var x = 1
proc one(): int = 1
echo "---- at runtime"
debug(1+1) # prints 1 + 1: 2
debug(x+1) # prints x + 1: 2
debug(one() + 1) # prints one() + 1: 2
What's the problem with toStrLit you have?
It works when the macro parameter is untyped,
thanks @rpowers and @mashingan, using untyped instead of typed works (but I was using typed because of your other answer from https://forum.nim-lang.org/t/3701/1#23070 to allow if x.getTypeInst.typeKind != ntyVoid: to make it work on empty arguments; I guess I need further processing to have both unmodified source AST and allow for void typed arguments)
That's actually a regular question (pain point?) regarding macros.
The current way is to preprocess using an untyped macro, and then dispatch/getAST from a typed macro when you need type resolution.
I remember seeing some discussion to ease that but they are pretty buried.