45 lines
1.8 KiB
Nim
45 lines
1.8 KiB
Nim
|
import std/macros
|
||
|
|
||
|
|
||
|
macro `<-`*(lhs: untyped, rhs: untyped): untyped =
|
||
|
# ensure that the left-hand side is a valid bracket expression e.g. [a, b, c]
|
||
|
let errmsg = "Invalid syntax for unpack operator: " & lhs.toStrLit().strVal()
|
||
|
if lhs.kind != nnkBracket:
|
||
|
error(errmsg, lhs)
|
||
|
for name in lhs:
|
||
|
if name.kind != nnkIdent:
|
||
|
error(errmsg, lhs)
|
||
|
|
||
|
# the result will be a check to ensure no index defects, followed by assignment statements
|
||
|
result = newNimNode(nnkStmtList, lineInfoFrom = lhs)
|
||
|
|
||
|
# first add a check to ensure that the container being unpacked has enough values
|
||
|
# this has to be done at runtime, since it's potentially unknown at compile time
|
||
|
let numIdents = lhs.len
|
||
|
let strRepr = lhs.toStrLit().strVal() & " <- " & rhs.toStrLit().strVal()
|
||
|
let lenCheck = quote do:
|
||
|
if `rhs`.len < `numIdents`:
|
||
|
raise newException(ValueError, "Not enough values to unpack: \"" & `strRepr` & "\"")
|
||
|
result.add(lenCheck)
|
||
|
|
||
|
var assign_from = rhs
|
||
|
# if the right-hand side is an expression, it might
|
||
|
# be expensive, so we save it to a temp variable
|
||
|
if rhs.kind != nnkIdent:
|
||
|
# repoint assign_src so that we end up using a temp variable
|
||
|
# instead of repeating the original expression a bunch of times
|
||
|
assign_from = ident("unpack_tmp_src")
|
||
|
# e.g. "let unpack_tmp_src = somestring.split()"
|
||
|
let declare_assign_src = newLetStmt(assign_from, rhs)
|
||
|
result.add(declare_assign_src)
|
||
|
|
||
|
# finally, inject the actual assignments
|
||
|
for i, name in lhs:
|
||
|
let assignment = quote do:
|
||
|
# expanded to e.g. "let a = someseq[0]"
|
||
|
let `name` = `assign_from`[`i`]
|
||
|
result.add(assignment)
|
||
|
|
||
|
|
||
|
proc showAddr*[T](x: T) = echo cast[uint64](unsafeAddr x)
|