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

Reply via email to