Ensure that .? chains work in generic code

This commit is contained in:
Mark Spanbroek 2021-05-04 15:55:05 +02:00
parent ebc7a2b48e
commit e8ab268758
3 changed files with 50 additions and 0 deletions

View File

@ -1,6 +1,12 @@
import std/options import std/options
import std/macros import std/macros
func isSym(node: NimNode): bool =
node.kind in {nnkSym, nnkOpenSymChoice, nnkClosedSymChoice}
func expectSym(node: NimNode) =
node.expectKind({nnkSym, nnkOpenSymChoice, nnkClosedSymChoice})
template `.?`*(option: typed, identifier: untyped{nkIdent}): untyped = template `.?`*(option: typed, identifier: untyped{nkIdent}): untyped =
# chain is of shape: option.?identifier # chain is of shape: option.?identifier
option ->? option.unsafeGet.identifier option ->? option.unsafeGet.identifier
@ -34,7 +40,17 @@ macro `.?`*(option: typed, call: untyped{nkCall}): untyped =
call[0] = right call[0] = right
call.insert(1, quote do: `option`.?`left`) call.insert(1, quote do: `option`.?`left`)
call call
elif procedure.isSym and $procedure == "[]":
# chain is of shape: option.?left[right] after semantic analysis
let left = call[1]
call[1] = quote do: `option`.?`left`
call
else: else:
# chain is of shape: option.?procedure(arguments) # chain is of shape: option.?procedure(arguments)
call.insert(1, quote do: `option`.unsafeGet) call.insert(1, quote do: `option`.unsafeGet)
quote do: `option` ->? `call` quote do: `option` ->? `call`
macro `.?`*(option: typed, symbol: untyped): untyped =
symbol.expectSym()
let expression = ident($symbol)
quote do: `option`.?`expression`

View File

@ -38,6 +38,23 @@ suite "optionals":
check a.?deduplicate()[0] + 1 == 42.some check a.?deduplicate()[0] + 1 == 42.some
check a.?deduplicate.map(x => x) == @[41, 42].some check a.?deduplicate.map(x => x) == @[41, 42].some
test ".? chains work in generic code":
proc test[T](a: ?T) =
check a.?len == 2.some
check a.?len.?uint8 == 2'u8.some
check a.?len() == 2.some
check a.?distribute(2).?len() == 2.some
check a.?len.unsafeGet == 2
check a.?len.unsafeGet.uint8.uint64 == 2'u64
check a.?len.unsafeGet() == 2
check a.?len.unsafeGet().uint8.uint64 == 2'u64
check a.?deduplicate()[0].?uint8.?uint64 == 41'u64.some
check a.?len + 1 == 3.some
check a.?deduplicate()[0] + 1 == 42.some
check a.?deduplicate.map(x => x) == @[41, 42].some
test @[41, 42].some
test "[] can be used for indexing optionals": test "[] can be used for indexing optionals":
let a: ?seq[int] = @[1, 2, 3].some let a: ?seq[int] = @[1, 2, 3].some
let b: ?seq[int] = seq[int].none let b: ?seq[int] = seq[int].none

View File

@ -41,6 +41,23 @@ suite "result":
check (a.?deduplicate()[0] + 1 == 42.success) check (a.?deduplicate()[0] + 1 == 42.success)
check (a.?deduplicate.map(x => x) == @[41, 42].success) check (a.?deduplicate.map(x => x) == @[41, 42].success)
test ".? chains work in generic code":
proc test[T](a: ?!T) =
check (a.?len == 2.success)
check (a.?len.?uint8 == 2'u8.success)
check (a.?len() == 2.success)
check (a.?distribute(2).?len() == 2.success)
check (a.?len.unsafeGet == 2)
check (a.?len.unsafeGet.uint8.uint64 == 2'u64)
check (a.?len.unsafeGet() == 2)
check (a.?len.unsafeGet().uint8.uint64 == 2'u64)
check (a.?deduplicate()[0].?uint8.?uint64 == 41'u64.success)
check (a.?len + 1 == 3.success)
check (a.?deduplicate()[0] + 1 == 42.success)
check (a.?deduplicate.map(x => x) == @[41, 42].success)
test @[41, 42].success
test "[] can be used for indexing results": test "[] can be used for indexing results":
let a: ?!seq[int] = @[1, 2, 3].success let a: ?!seq[int] = @[1, 2, 3].success
let b: ?!seq[int] = seq[int].failure error let b: ?!seq[int] = seq[int].failure error