From 2b5e48d80735be60c68023de114a586bbcc18360 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 7 Mar 2019 00:49:39 +0100 Subject: move assertions and iterators out of system.nim (#10597) * move assertions and iterators out of system.nim * limit nimsuggest tests to the first 3 suggestions --- lib/system/assertions.nim | 89 +++++++++++++++++ lib/system/fatal.nim | 47 +++++++++ lib/system/iterators.nim | 243 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+) create mode 100644 lib/system/assertions.nim create mode 100644 lib/system/fatal.nim create mode 100644 lib/system/iterators.nim (limited to 'lib/system') diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim new file mode 100644 index 000000000..42a5b61d7 --- /dev/null +++ b/lib/system/assertions.nim @@ -0,0 +1,89 @@ +include "system/helpers" + +when not declared(sysFatal): + include "system/fatal" + + +proc raiseAssert*(msg: string) {.noinline, noReturn.} = + sysFatal(AssertionError, msg) + +proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = + # trick the compiler to not list ``AssertionError`` when called + # by ``assert``. + type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, + tags: [].} + Hide(raiseAssert)(msg) + +template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) = + const loc = $instantiationInfo(-1, true) + bind instantiationInfo + mixin failedAssertImpl + when enabled: + # for stacktrace; fixes #8928 ; Note: `fullPaths = true` is correct + # here, regardless of --excessiveStackTrace + {.line: instantiationInfo(fullPaths = true).}: + if not cond: + failedAssertImpl(loc & " `" & expr & "` " & msg) + +template assert*(cond: untyped, msg = "") = + ## Raises ``AssertionError`` with `msg` if `cond` is false. Note + ## that ``AssertionError`` is hidden from the effect system, so it doesn't + ## produce ``{.raises: [AssertionError].}``. This exception is only supposed + ## to be caught by unit testing frameworks. + ## + ## The compiler may not generate any code at all for ``assert`` if it is + ## advised to do so through the ``-d:release`` or ``--assertions:off`` + ## `command line switches `_. + const expr = astToStr(cond) + assertImpl(cond, msg, expr, compileOption("assertions")) + +template doAssert*(cond: untyped, msg = "") = + ## same as ``assert`` but is always turned on regardless of ``--assertions`` + const expr = astToStr(cond) + assertImpl(cond, msg, expr, true) + +template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = + ## Sets an assertion failure handler that will intercept any assert + ## statements following `onFailedAssert` in the current module scope. + ## + ## .. code-block:: nim + ## # module-wide policy to change the failed assert + ## # exception type in order to include a lineinfo + ## onFailedAssert(msg): + ## var e = new(TMyError) + ## e.msg = msg + ## e.lineinfo = instantiationInfo(-2) + ## raise e + ## + template failedAssertImpl(msgIMPL: string): untyped {.dirty.} = + let msg = msgIMPL + code + +template doAssertRaises*(exception: typedesc, code: untyped): typed = + ## Raises ``AssertionError`` if specified ``code`` does not raise the + ## specified exception. Example: + ## + ## .. code-block:: nim + ## doAssertRaises(ValueError): + ## raise newException(ValueError, "Hello World") + var wrong = false + when Exception is exception: + try: + if true: + code + wrong = true + except Exception: + discard + else: + try: + if true: + code + wrong = true + except exception: + discard + except Exception: + raiseAssert(astToStr(exception) & + " wasn't raised, another error was raised instead by:\n"& + astToStr(code)) + if wrong: + raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim new file mode 100644 index 000000000..100b6d482 --- /dev/null +++ b/lib/system/fatal.nim @@ -0,0 +1,47 @@ +{.push profiler: off.} +when hostOS == "standalone": + include "$projectpath/panicoverride" + + proc sysFatal(exceptn: typedesc, message: string) {.inline.} = + panic(message) + + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} = + rawoutput(message) + panic(arg) + +elif defined(nimQuirky) and not defined(nimscript): + proc name(t: typedesc): string {.magic: "TypeTrait".} + + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} = + var buf = newStringOfCap(200) + add(buf, "Error: unhandled exception: ") + add(buf, message) + add(buf, arg) + add(buf, " [") + add(buf, name exceptn) + add(buf, "]") + cstderr.rawWrite buf + quit 1 + + proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} = + sysFatal(exceptn, message, "") + +else: + proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} = + when declared(owned): + var e: owned(ref exceptn) + else: + var e: ref exceptn + new(e) + e.msg = message + raise e + + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} = + when declared(owned): + var e: owned(ref exceptn) + else: + var e: ref exceptn + new(e) + e.msg = message & arg + raise e +{.pop.} diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim new file mode 100644 index 000000000..2812db69e --- /dev/null +++ b/lib/system/iterators.nim @@ -0,0 +1,243 @@ +iterator items*[T](a: openArray[T]): T {.inline.} = + ## iterates over each item of `a`. + var i = 0 + while i < len(a): + yield a[i] + inc(i) + +iterator mitems*[T](a: var openArray[T]): var T {.inline.} = + ## iterates over each item of `a` so that you can modify the yielded value. + var i = 0 + while i < len(a): + yield a[i] + inc(i) + +iterator items*[IX, T](a: array[IX, T]): T {.inline.} = + ## iterates over each item of `a`. + var i = low(IX) + if i <= high(IX): + while true: + yield a[i] + if i >= high(IX): break + inc(i) + +iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} = + ## iterates over each item of `a` so that you can modify the yielded value. + var i = low(IX) + if i <= high(IX): + while true: + yield a[i] + if i >= high(IX): break + inc(i) + +iterator items*[T](a: set[T]): T {.inline.} = + ## iterates over each element of `a`. `items` iterates only over the + ## elements that are really in the set (and not over the ones the set is + ## able to hold). + var i = low(T).int + while i <= high(T).int: + if T(i) in a: yield T(i) + inc(i) + +iterator items*(a: cstring): char {.inline.} = + ## iterates over each item of `a`. + when defined(js): + var i = 0 + var L = len(a) + while i < L: + yield a[i] + inc(i) + else: + var i = 0 + while a[i] != '\0': + yield a[i] + inc(i) + +iterator mitems*(a: var cstring): var char {.inline.} = + ## iterates over each item of `a` so that you can modify the yielded value. + var i = 0 + while a[i] != '\0': + yield a[i] + inc(i) + +iterator items*(E: typedesc[enum]): E = + ## iterates over the values of the enum ``E``. + for v in low(E)..high(E): + yield v + +iterator items*[T](s: HSlice[T, T]): T = + ## iterates over the slice `s`, yielding each value between `s.a` and `s.b` + ## (inclusively). + for x in s.a..s.b: + yield x + +iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator mpairs*[T](a: var openArray[T]): tuple[key:int, val:var T]{.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = low(IX) + if i <= high(IX): + while true: + yield (i, a[i]) + if i >= high(IX): break + inc(i) + +iterator mpairs*[IX, T](a:var array[IX, T]):tuple[key:IX,val:var T] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = low(IX) + if i <= high(IX): + while true: + yield (i, a[i]) + if i >= high(IX): break + inc(i) + +iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = 0 + while i < len(a): + yield (i, a[i]) + inc(i) + +iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = 0 + while a[i] != '\0': + yield (i, a[i]) + inc(i) + +iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = + ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = 0 + while a[i] != '\0': + yield (i, a[i]) + inc(i) + +iterator items*[T](a: seq[T]): T {.inline.} = + ## iterates over each item of `a`. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "seq modified while iterating over it") + +iterator mitems*[T](a: var seq[T]): var T {.inline.} = + ## iterates over each item of `a` so that you can modify the yielded value. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "seq modified while iterating over it") + +iterator items*(a: string): char {.inline.} = + ## iterates over each item of `a`. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "string modified while iterating over it") + +iterator mitems*(a: var string): var char {.inline.} = + ## iterates over each item of `a` so that you can modify the yielded value. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "string modified while iterating over it") + +iterator fields*[T: tuple|object](x: T): RootObj {. + magic: "Fields", noSideEffect.} + ## iterates over every field of `x`. Warning: This really transforms + ## the 'for' and unrolls the loop. The current implementation also has a bug + ## that affects symbol binding in the loop body. + +iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: untyped] {. + magic: "Fields", noSideEffect.} + ## iterates over every field of `x` and `y`. + ## Warning: This is really transforms the 'for' and unrolls the loop. + ## The current implementation also has a bug that affects symbol binding + ## in the loop body. + +iterator fieldPairs*[T: tuple|object](x: T): RootObj {. + magic: "FieldPairs", noSideEffect.} + ## Iterates over every field of `x` returning their name and value. + ## + ## When you iterate over objects with different field types you have to use + ## the compile time ``when`` instead of a runtime ``if`` to select the code + ## you want to run for each type. To perform the comparison use the `is + ## operator `_. Example: + ## + ## .. code-block:: Nim + ## + ## type + ## Custom = object + ## foo: string + ## bar: bool + ## + ## proc `$`(x: Custom): string = + ## result = "Custom:" + ## for name, value in x.fieldPairs: + ## when value is bool: + ## result.add("\n\t" & name & " is " & $value) + ## else: + ## if value.isNil: + ## result.add("\n\t" & name & " (nil)") + ## else: + ## result.add("\n\t" & name & " '" & value & "'") + ## + ## Another way to do the same without ``when`` is to leave the task of + ## picking the appropriate code to a secondary proc which you overload for + ## each field type and pass the `value` to. + ## + ## Warning: This really transforms the 'for' and unrolls the loop. The + ## current implementation also has a bug that affects symbol binding in the + ## loop body. + +iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ + a, b: untyped] {. + magic: "FieldPairs", noSideEffect.} + ## iterates over every field of `x` and `y`. + ## Warning: This really transforms the 'for' and unrolls the loop. + ## The current implementation also has a bug that affects symbol binding + ## in the loop body. -- cgit 1.4.1-2-gfad0