mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-07 00:03:10 +00:00
Handle bind (=?) errors in without statements differently
Keeps track of the current error variable at compile time, instead of using a pointer to the error variable at runtime. Employs a trick with an unused type parameter to ensure that invocations of the bindFailed() macro are expanded after captureBindError() is expanded.
This commit is contained in:
parent
1f0afff48b
commit
d463d491cc
@ -1,24 +1,43 @@
|
|||||||
import std/options
|
import std/options
|
||||||
|
import std/macros
|
||||||
|
|
||||||
var captures {.global, compileTime.}: int
|
# A stack of names of error variables. Keeps track of the error variables that
|
||||||
var errorVariable {.threadvar.}: ptr ref CatchableError
|
# are given to captureBindError().
|
||||||
|
var errorVariableNames {.global, compileTime.}: seq[string]
|
||||||
|
|
||||||
template captureBindError*(error: var ref CatchableError, expression): auto =
|
macro captureBindError*(error: var ref CatchableError, expression): auto =
|
||||||
let previousErrorVariable = errorVariable
|
## Ensures that an error is assigned to the error variable when a binding (=?)
|
||||||
errorVariable = addr error
|
## fails inside the expression.
|
||||||
|
|
||||||
static: inc captures
|
|
||||||
let evaluated = expression
|
|
||||||
static: dec captures
|
|
||||||
|
|
||||||
errorVariable = previousErrorVariable
|
|
||||||
|
|
||||||
|
# name of the error variable as a string literal
|
||||||
|
let errorVariableName = newLit($error)
|
||||||
|
quote do:
|
||||||
|
# add error variable to the top of the stack
|
||||||
|
static: errorVariableNames.add(`errorVariableName`)
|
||||||
|
# evaluate the expression
|
||||||
|
let evaluated = `expression`
|
||||||
|
# pop error variable from the stack
|
||||||
|
static: discard errorVariableNames.pop()
|
||||||
|
# return the evaluated result
|
||||||
evaluated
|
evaluated
|
||||||
|
|
||||||
func error[T](option: Option[T]): ref CatchableError =
|
func error[T](option: Option[T]): ref CatchableError =
|
||||||
newException(ValueError, "Option is set to `none`")
|
newException(ValueError, "Option is set to `none`")
|
||||||
|
|
||||||
template bindFailed*(expression) =
|
macro bindFailed*(expression; _: type = void) =
|
||||||
when captures > 0:
|
## Called when a binding (=?) fails.
|
||||||
mixin error
|
## Assigns an error to the error variable (specified in captureBindError())
|
||||||
errorVariable[] = expression.error
|
## when appropriate.
|
||||||
|
|
||||||
|
# This macro has a type parameter to ensure that the compiler does not
|
||||||
|
# expand it before it expands invocations of captureBindError().
|
||||||
|
|
||||||
|
# check that we have an error variable on the stack
|
||||||
|
if errorVariableNames.len > 0:
|
||||||
|
# create an identifier that references the current error variable
|
||||||
|
let errorVariable = ident errorVariableNames[^1]
|
||||||
|
return quote do:
|
||||||
|
# check that the error variable is in scope
|
||||||
|
when compiles(`errorVariable`):
|
||||||
|
# assign bind error to error variable
|
||||||
|
`errorVariable` = `expression`.error
|
||||||
|
|||||||
@ -412,6 +412,17 @@ suite "result":
|
|||||||
for i in 0..<1000:
|
for i in 0..<1000:
|
||||||
spawn fail(i)
|
spawn fail(i)
|
||||||
|
|
||||||
|
test "without statement doesn't interfere with generic code called elsewhere":
|
||||||
|
proc foo(_: type): ?!int =
|
||||||
|
if error =? success(1).errorOption:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc bar {.used.} = # defined, but not used
|
||||||
|
without x =? bool.foo(), error:
|
||||||
|
discard error
|
||||||
|
|
||||||
|
discard bool.foo() # same type parameter 'bool' as used in bar()
|
||||||
|
|
||||||
test "catch can be used to convert exceptions to results":
|
test "catch can be used to convert exceptions to results":
|
||||||
check parseInt("42").catch == 42.success
|
check parseInt("42").catch == 42.success
|
||||||
check parseInt("foo").catch.error of ValueError
|
check parseInt("foo").catch.error of ValueError
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user