Use `scope` instead of `block`

To avoid influencing `break` statements.
This commit is contained in:
Mark Spanbroek 2022-10-24 13:02:55 +02:00
parent 1dcef4b302
commit d079162675
7 changed files with 43 additions and 6 deletions

View File

@ -4,6 +4,7 @@ description = "Elegant optional types"
license = "MIT"
task test, "Runs the test suite":
exec "nim c -r tests/testScope"
for module in ["options", "result", "stew"]:
withDir "testmodules/" & module:
delEnv "NIMBLE_DIR" # use nimbledeps dir

View File

@ -1,6 +1,7 @@
import std/options
import std/macros
import std/strformat
import ./private/scope
func isSym(node: NimNode): bool =
node.kind in {nnkSym, nnkOpenSymChoice, nnkClosedSymChoice}
@ -70,6 +71,6 @@ template `.?`*(left: typed, right: untyped): untyped =
## The `.?` chaining operator is used to safely access fields and call procs
## on Options or Results. The expression is only evaluated when the preceding
## Option or Result has a value.
block:
scope:
let evaluated = left
chain(evaluated, right)

View File

@ -1,10 +1,11 @@
import std/macros
import ./private/scope
macro `.?`*(expression: typed, brackets: untyped{nkBracket}): untyped =
# chain is of shape: expression.?[index]
let index = brackets[0]
quote do:
block:
scope:
type T = typeof(`expression`[`index`])
try:
`expression`[`index`].some

View File

@ -1,19 +1,21 @@
import ./private/scope
template liftUnary*(T: type, operator: untyped) =
template `operator`*(a: T): untyped =
block:
scope:
let evaluated = a
evaluated ->? `operator`(evaluated.unsafeGet())
template liftBinary*(T: type, operator: untyped) =
template `operator`*(a: T, b: T): untyped =
block:
scope:
let evalA = a
let evalB = b
(evalA, evalB) ->? `operator`(evalA.unsafeGet, evalB.unsafeGet)
template `operator`*(a: T, b: typed): untyped =
block:
scope:
let evalA = a
evalA ->? `operator`(evalA.unsafeGet(), b)

View File

@ -7,6 +7,7 @@ import ./operators
import ./without
include ./private/errorban
import ./private/scope
export options except get
export binding
@ -56,7 +57,7 @@ proc `|?`*[T](option: ?T, fallback: T): T =
macro `.?`*[T](option: ?T, brackets: untyped{nkBracket}): untyped =
let index = brackets[0]
quote do:
block:
scope:
let evaluated = `option`
type U = typeof(evaluated.unsafeGet().?[`index`].unsafeGet())
if evaluated.isSome:

View File

@ -0,0 +1,14 @@
proc neverhappens {.inline, noreturn.} =
discard
template scope*(body): untyped =
## Can be used instead of `block` to introduce a new scoped block of code,
## without influencing any `break` statements in the code.
##
## See also: https://github.com/nim-lang/RFCs/issues/451
if true:
body
else:
# call {.noreturn.} proc here to ensure that the compiler uses `body` for
# the result value of the if-statement
neverhappens()

17
tests/testScope.nim Normal file
View File

@ -0,0 +1,17 @@
import ../questionable/private/scope
import std/unittest
suite "Scope":
test "introduces variable scope":
var x = 1
scope:
var x: string
x = "some string"
check x == "some string"
check x == 1
test "returns value":
let x = scope:
3
check x == 3