From d079162675b917b76df4cdd83a2cb993cdff8612 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 24 Oct 2022 13:02:55 +0200 Subject: [PATCH] Use `scope` instead of `block` To avoid influencing `break` statements. --- questionable.nimble | 1 + questionable/chaining.nim | 3 ++- questionable/indexing.nim | 3 ++- questionable/operators.nim | 8 +++++--- questionable/options.nim | 3 ++- questionable/private/scope.nim | 14 ++++++++++++++ tests/testScope.nim | 17 +++++++++++++++++ 7 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 questionable/private/scope.nim create mode 100644 tests/testScope.nim diff --git a/questionable.nimble b/questionable.nimble index 26b7fb0..800078c 100644 --- a/questionable.nimble +++ b/questionable.nimble @@ -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 diff --git a/questionable/chaining.nim b/questionable/chaining.nim index e2c97cc..bf08bf3 100644 --- a/questionable/chaining.nim +++ b/questionable/chaining.nim @@ -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) diff --git a/questionable/indexing.nim b/questionable/indexing.nim index e265967..3dfe43c 100644 --- a/questionable/indexing.nim +++ b/questionable/indexing.nim @@ -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 diff --git a/questionable/operators.nim b/questionable/operators.nim index c163509..7576cf5 100644 --- a/questionable/operators.nim +++ b/questionable/operators.nim @@ -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) diff --git a/questionable/options.nim b/questionable/options.nim index 7d15bd9..6af81be 100644 --- a/questionable/options.nim +++ b/questionable/options.nim @@ -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: diff --git a/questionable/private/scope.nim b/questionable/private/scope.nim new file mode 100644 index 0000000..87f5fed --- /dev/null +++ b/questionable/private/scope.nim @@ -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() diff --git a/tests/testScope.nim b/tests/testScope.nim new file mode 100644 index 0000000..212e2aa --- /dev/null +++ b/tests/testScope.nim @@ -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