summary refs log tree commit diff stats
path: root/lib/system
diff options
context:
space:
mode:
authorMiran <narimiran@disroot.org>2019-03-07 00:49:39 +0100
committerAndreas Rumpf <rumpf_a@web.de>2019-03-07 00:49:39 +0100
commit2b5e48d80735be60c68023de114a586bbcc18360 (patch)
tree4031d089c40c86339aa2a9b15b9f47840c473400 /lib/system
parent26f48437ca429f8e25f20d1f986c5b46e83cc90b (diff)
downloadNim-2b5e48d80735be60c68023de114a586bbcc18360.tar.gz
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
Diffstat (limited to 'lib/system')
-rw-r--r--lib/system/assertions.nim89
-rw-r--r--lib/system/fatal.nim47
-rw-r--r--lib/system/iterators.nim243
3 files changed, 379 insertions, 0 deletions
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 <nimc.html#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 <manual.html#generics-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.