Hi all, does anyone know a way to perform tuple unpacking using an array rvalue rather than a tuple rvalue?
For example, here is tuple unpacking:
type T3 = tuple[d, e, f: int]
var t3: T3 = (7, 9, 12)
var (x, y, z) = t3 # <-- tuple unpacking
echo($z, $y, $x)
But I'd like to do it with an array of known type, eg:
type A3 = array[0..2, int]
var a3: A3 = [7, 9, 12]
var (x, y, z) = a3 # <-- This doesn't work. :(
echo($z, $y, $x)
Does anyone know if this can be done, and if so, how?
I'm happy to use an unpack proc if necessary, so that the third line would become:
var (x, y, z) = unpack(a3)
The closest I can find in the forums is this template suggested by @gradha, but it doesn't generalise to a variable number of parameters.
Thanks for any help.
Here's a macro that does what you want. It also handles arrays like array[-10 .. -5, T] thanks to type info, relies on nimrod from devel branch.
import macros
proc range_interval (n:NimNode): seq[NimNode] {.compileTime.} =
newSeq result,0
if n.typeKind == ntyRange:
#echo n.treerepr
let a = n[1].intval.int
let b = n[2].intval.int
for i in a .. b:
result.add newLit(i)
elif n.typeKind == ntyEnum:
assert n[0].kind == nnkEnumTy
for f in n[0].children:
result.add f
else:
echo treerepr(n)
quit 1
macro asTuple* (obj:stmt): expr =
let ty = obj.getType()
if ty.typekind in {ntyArray, ntyArrayConstr}:
let range_type = ty[1].getType
let keys = range_interval(range_type)
assert keys.len > 0
result = newNimNode(nnkPar)
for k in keys:
result.add(quote do: `obj`[`k`])
else:
echo "unhandled type for asTuple()"
echo obj.treerepr
quit 1
when defined(Debug):
echo result.treerepr
echo result.repr
when isMainModule:
block:
let obj = [1,2,3]
let (x,y,z) = asTuple(obj)
assert x == 1
assert y == 2
assert z == 3
block:
let (x,y,z) = asTuple([1,2,3])
assert x == 1
assert y == 2
assert z == 3
block:
var arr: array[-9 .. 0, int]
arr[-9] = 1
arr[-8] = 2
let (a,b,c,d,e, f,g,h,i,j) = asTuple(arr)
assert a == 1
assert b == 2
assert c == 0
block:
type TE = enum
a = -1, b, c
var arr: array[TE, int]
arr[a] = 1
arr[b] = 2
arr[c] = 3
let (aa,bb,cc) = asTuple(arr)
assert aa == 1
assert bb == 2
assert cc == 3
when false:
# fails atm
var arr2: array[a..b, int]
arr2[a] = 1
arr2[b] = 2
let (a2,b2) = asTuple(arr2)
assert a2 == 1
assert b2 == 2
Amazing, thank you @fowl!
I assume that .getType and .typeKind are new in the devel branch, post-0.10.2?
I've never seen any evidence of them in the macros module documentation. I just re-built the devel branch of the Nim compiler, and I can find them in lib/core/macros.nim.
Were .getType and .typeKind present in 0.10.2 under different names, or are they completely new?
Thanks again!
Ah, great, thanks @Araq!
I'll definitely be making use of these two new procs.
(I've spent some time scratching my head over the past few months, trying to work out how to get access to TNimrodTypeKind & TNimrodSymKind. I assumed something was missing/incomplete, but I also assumed the Nim devs already know about it and are working on it. Plus, you've mentioned your intentions wrt stmt vs expr.)
Will there also be a .symKind soon?
@jboy you can retrieve the type of a symbol with getType()!
import macros
macro showExpr(body:stmt): expr =
echo treerepr body
if body.kind == nnkInfix:
for child in body.children:
echo child.repr, " : ", child.getType.typeKind
echo body.repr, " : ", body.getType.typeKind
result = body
when isMainModule:
let y = 1
let x = showExpr(y * 2)
echo x
outputs the expression passed to it and the types involved
Infix
Sym "*"
Sym "y"
IntLit 2
* : ntyProc
y : ntyInt
2 : ntyInt
y * 2 : ntyInt
Hi @fowl, I was actually after the NimSymKind of a symbol, which AFAICT is different to the NimTypeKind (returned by typeKind) or the NimNodeKind (returned by kind). :)
For example, I expect that the NimSymKind of y in your example would be nskLet, * would be nskProc (or perhaps nskTemplate), and 2 wouldn't be a symbol, so wouldn't have a NimSymKind.
In the macros module, the proc genSym takes a NimSymKind as an argument, but grep tells me that there are no procs that return a NimSymKind.
What's the current status of unpacking arrays? I've looked around, but haven't seen anything more recent than this thread. The code by @fowl currently breaks with the following error:
unpack.nim(19, 21) Warning: stmt is deprecated [Deprecated]
unpack.nim(19, 28) Warning: expr is deprecated [Deprecated]
unpack.nim(21, 34) Error: undeclared identifier: 'ntyArrayConstr'
> Process terminated with exit code 1
macros.nim only defines ntyArray
There is also ntyOpenArray