Allow indexing of tables with ?[]

This commit is contained in:
Mark Spanbroek 2021-03-18 17:58:48 +01:00
parent 91f97c73ef
commit 40f5b4f1b8
3 changed files with 63 additions and 0 deletions

41
questionable/indexing.nim Normal file
View File

@ -0,0 +1,41 @@
import std/macros
macro `?`*(expression: typed, brackets: untyped{nkBracket}): untyped =
# chain is of shape: expression?[index]
let index = brackets[0]
quote do:
type T = typeof(`expression`[`index`])
try:
`expression`[`index`].some
except KeyError:
T.none
macro `?`*(expression: typed, infix: untyped{nkInfix}): untyped =
# chain is of shape: expression?left `operator` right
let left = infix[1]
infix[1] = quote do: `expression`?`left`
infix
macro `?`*(expression: typed, bracket: untyped{nkBracketExpr}): untyped =
# chain is of shape: expression?left[right]
let left = bracket[0]
bracket[0] = quote do: `expression`?`left`
bracket
macro `?`*(expression: typed, dot: untyped{nkDotExpr}): untyped =
# chain is of shape: expression?left.right
let left = dot[0]
dot[0] = quote do: `expression`?`left`
dot
macro `?`*(expression: typed, call: untyped{nkCall}): untyped =
let procedure = call[0]
if procedure.kind == nnkDotExpr:
# chain is of shape: expression?left.right(arguments)
let (left, right) = (procedure[0], procedure[1])
call[0] = right
call.insert(1, quote do: `expression`?`left`)
call
else:
call.expectKind(nnkBracketExpr)
nil

View File

@ -1,12 +1,14 @@
import std/options
import std/macros
import ./chaining
import ./indexing
import ./operators
include ./errorban
export options
export chaining
export indexing
template `?`*(T: typed): type Option[T] =
Option[T]

View File

@ -1,5 +1,6 @@
import std/unittest
import std/sequtils
import std/tables
import std/sugar
import pkg/questionable
@ -87,6 +88,25 @@ suite "optionals":
if var a =? int.none:
fail
test "?[] can be used for indexing tables without raising KeyError":
let table = @{"a": 1, "b": 2}.toTable
check table?["a"] == 1.some
check table?["c"] == int.none
test "?[] can be followed by calls, operators and indexing":
let table = @{"a": @[41, 42]}.toTable
check table?["a"].isSome
check table?["a"].isSome()
check table?["a"][0] == 41.some
check table?["a"]?.len.get == 2
check table?["a"]?.len.get.uint8.uint64 == 2'u64
check table?["a"]?.len.get() == 2
check table?["a"]?.len.get().uint8.uint64 == 2'u64
check table?["a"]?.deduplicate()[0]?.uint8?.uint64 == 41'u64.some
check table?["a"]?.len + 1 == 3.some
check table?["a"]?.deduplicate()[0] + 1 == 42.some
check table?["a"]?.deduplicate.map(x => x) == @[41, 42].some
test "=? evaluates optional expression only once":
var count = 0
if a =? (inc count; 42.some):