Finally got around to test my idea above and here's what it looks like:
import macros
proc addTupleToCall(c: NimNode, t: NimNode, temps: NimNode) =
let typ = getType(t)
var s = t
if t.kind != nnkSym:
# If it's something more complex than sym, store it in
# tmp to avoid double evaluation
s = genSym(nskLet, "tmp")
temps.add newIdentDefs(s, newEmptyNode(), t)
var a = 0
var b = 0
if $typ[0] == "tuple":
b = typ.len - 2
elif $typ[0] == "array":
a = typ[1][1].intVal
b = typ[1][2].intVal
else:
doAssert(false, "Wrong type")
for i in a .. b:
c.add(newTree(nnkBracketExpr, s, newLit(i)))
macro addToCall(a: untyped, b: varargs[typed]): untyped =
let tmps = newNimNode(nnkLetSection)
var isTuple = false
for i in 0 ..< b.len:
if i mod 2 == 0:
isTuple = bool(b[i].intVal)
else:
if isTuple:
addTupleToCall(a, b[i], tmps)
else:
a.add(b[i])
newTree(nnkStmtList, tmps, a)
proc addToCallAux(a: NimNode, b: NimNode, isTuple: bool): NimNode =
var args = @[b, newLit(isTuple)]
var n = a
while n.kind == nnkInfix and n[0].kind == nnkIdent:
if $n[0] == "<<<":
# echo 1
args.add(n[2])
args.add(newLit(false))
elif $n[0] == "<<*":
args.add(n[2])
args.add(newLit(true))
n = n[1]
result = newCall(bindSym"addToCall", n)
for i in countdown(args.high, 0):
result.add(args[i])
macro `<<<`*(a: untyped, b: untyped): untyped =
addToCallAux(a, b, false)
macro `<<*`*(a: untyped, b: untyped): untyped =
addToCallAux(a, b, true)
when isMainModule:
proc foo(a, b: int) =
echo "foo ", a, " ", b
echo() <<< 1 <<< 2 <<* (3, 4) <<* [5, 6] <<< 7
foo() <<< 5 <<< 6
Run