advent/2020/lib/util.nim

45 lines
1.8 KiB
Nim
Raw Normal View History

2021-12-01 01:09:36 +00:00
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)