summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDean Thompson <deansherthompson@gmail.com>2019-01-21 16:40:04 -0500
committerGitHub <noreply@github.com>2019-01-21 16:40:04 -0500
commita6de0274ee768d135bab280d2b2700a0bb475300 (patch)
tree176b91e837ab7d2217713665c16fc28163017960
parent5b39c7aca91c1d20eb81425cf8f3854876aed475 (diff)
parentee89ba6bdb664fe4972f2917499cff1afdac0bab (diff)
downloadNim-a6de0274ee768d135bab280d2b2700a0bb475300.tar.gz
Merge pull request #1 from nim-lang/devel
Getting the latest from nim-lang
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md24
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.md17
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml26
-rw-r--r--appveyor.yml18
-rw-r--r--build_all.sh5
-rw-r--r--changelog.md54
-rw-r--r--compiler.nimble2
-rw-r--r--compiler/asciitables.nim83
-rw-r--r--compiler/ast.nim7
-rw-r--r--compiler/ccgexprs.nim2
-rw-r--r--compiler/ccgmerge.nim1
-rw-r--r--compiler/ccgtypes.nim41
-rw-r--r--compiler/cgen.nim264
-rw-r--r--compiler/cgendata.nim10
-rw-r--r--compiler/cmdlinehelper.nim19
-rw-r--r--compiler/commands.nim74
-rw-r--r--compiler/destroyer.nim23
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/docgen.nim17
-rw-r--r--compiler/extccomp.nim20
-rw-r--r--compiler/guards.nim8
-rw-r--r--compiler/importer.nim5
-rw-r--r--compiler/jsgen.nim78
-rw-r--r--compiler/lexer.nim62
-rw-r--r--compiler/lineinfos.nim4
-rw-r--r--compiler/lists.nim28
-rw-r--r--compiler/lookups.nim8
-rw-r--r--compiler/main.nim7
-rw-r--r--compiler/modulepaths.nim2
-rw-r--r--compiler/msgs.nim24
-rw-r--r--compiler/options.nim18
-rw-r--r--compiler/parampatterns.nim15
-rw-r--r--compiler/parser.nim106
-rw-r--r--compiler/passes.nim6
-rw-r--r--compiler/pathutils.nim19
-rw-r--r--compiler/pragmas.nim43
-rw-r--r--compiler/scriptconfig.nim19
-rw-r--r--compiler/sem.nim2
-rw-r--r--compiler/semasgn.nim160
-rw-r--r--compiler/semcall.nim13
-rw-r--r--compiler/semdata.nim7
-rw-r--r--compiler/semexprs.nim53
-rw-r--r--compiler/semfields.nim3
-rw-r--r--compiler/semfold.nim4
-rw-r--r--compiler/semmagic.nim7
-rw-r--r--compiler/semstmts.nim173
-rw-r--r--compiler/semtypes.nim100
-rw-r--r--compiler/sigmatch.nim18
-rw-r--r--compiler/sizealignoffsetimpl.nim41
-rw-r--r--compiler/suggest.nim17
-rw-r--r--compiler/types.nim35
-rw-r--r--compiler/unittest_light.nim38
-rw-r--r--compiler/vm.nim42
-rw-r--r--compiler/vmdef.nim2
-rw-r--r--compiler/vmgen.nim40
-rw-r--r--compiler/vmops.nim5
-rw-r--r--config/config.nims1
-rw-r--r--config/nim.cfg18
-rw-r--r--doc/advopt.txt38
-rw-r--r--doc/basicopt.txt4
-rw-r--r--doc/docs.rst3
-rw-r--r--doc/lib.rst24
-rw-r--r--doc/manual.rst18
-rw-r--r--doc/nimc.rst5
-rw-r--r--doc/nims.rst36
-rw-r--r--koch.nim104
-rw-r--r--lib/core/macros.nim33
-rw-r--r--lib/core/seqs.nim6
-rw-r--r--lib/deprecated/core/unsigned.nim18
-rw-r--r--lib/deprecated/pure/actors.nim239
-rw-r--r--lib/deprecated/pure/actors.nim.cfg3
-rw-r--r--lib/deprecated/pure/ftpclient.nim1
-rw-r--r--lib/deprecated/pure/parseurl.nim112
-rw-r--r--lib/deprecated/pure/rawsockets.nim14
-rw-r--r--lib/deprecated/pure/securehash.nim (renamed from lib/pure/securehash.nim)0
-rw-r--r--lib/experimental/diff.nim14
-rw-r--r--lib/impure/db_odbc.nim2
-rw-r--r--lib/impure/db_sqlite.nim26
-rw-r--r--lib/impure/nre.nim8
-rw-r--r--lib/impure/re.nim27
-rw-r--r--lib/impure/ssl.nim99
-rw-r--r--lib/js/dom.nim30
-rw-r--r--lib/js/jscore.nim12
-rw-r--r--lib/js/jsffi.nim6
-rw-r--r--lib/nimbase.h4
-rw-r--r--lib/packages/docutils/rst.nim73
-rw-r--r--lib/packages/docutils/rstast.nim3
-rw-r--r--lib/packages/docutils/rstgen.nim4
-rw-r--r--lib/posix/posix_linux_amd64_consts.nim1
-rw-r--r--lib/posix/posix_nintendoswitch_consts.nim1
-rw-r--r--lib/posix/posix_other_consts.nim1
-rw-r--r--lib/pure/asynchttpserver.nim48
-rw-r--r--lib/pure/base64.nim84
-rw-r--r--lib/pure/cgi.nim5
-rw-r--r--lib/pure/collections/critbits.nim6
-rw-r--r--lib/pure/collections/deques.nim308
-rw-r--r--lib/pure/collections/intsets.nim66
-rw-r--r--lib/pure/collections/lists.nim533
-rw-r--r--lib/pure/collections/queues.nim257
-rw-r--r--lib/pure/collections/sequtils.nim712
-rw-r--r--lib/pure/collections/sets.nim7
-rw-r--r--lib/pure/collections/tableimpl.nim2
-rw-r--r--lib/pure/collections/tables.nim2699
-rw-r--r--lib/pure/concurrency/atomics.nim378
-rw-r--r--lib/pure/distros.nim11
-rw-r--r--lib/pure/httpclient.nim243
-rw-r--r--lib/pure/httpserver.nim535
-rw-r--r--lib/pure/includes/osenv.nim2
-rw-r--r--lib/pure/includes/oserr.nim4
-rw-r--r--lib/pure/includes/osseps.nim4
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim22
-rw-r--r--lib/pure/json.nim34
-rw-r--r--lib/pure/logging.nim7
-rw-r--r--lib/pure/matchers.nim68
-rw-r--r--lib/pure/math.nim516
-rw-r--r--lib/pure/memfiles.nim26
-rw-r--r--lib/pure/nativesockets.nim70
-rw-r--r--lib/pure/net.nim77
-rw-r--r--lib/pure/os.nim243
-rw-r--r--lib/pure/osproc.nim39
-rw-r--r--lib/pure/parsecfg.nim2
-rw-r--r--lib/pure/parseopt.nim29
-rw-r--r--lib/pure/parseopt2.nim17
-rw-r--r--lib/pure/parsesql.nim2
-rw-r--r--lib/pure/parseutils.nim301
-rw-r--r--lib/pure/parsexml.nim2
-rw-r--r--lib/pure/pathnorm.nim24
-rw-r--r--lib/pure/random.nim50
-rw-r--r--lib/pure/scgi.nim295
-rw-r--r--lib/pure/selectors.nim3
-rw-r--r--lib/pure/streams.nim6
-rw-r--r--lib/pure/strtabs.nim31
-rw-r--r--lib/pure/strutils.nim2032
-rw-r--r--lib/pure/subexes.nim406
-rw-r--r--lib/pure/terminal.nim2
-rw-r--r--lib/pure/times.nim815
-rw-r--r--lib/pure/typetraits.nim49
-rw-r--r--lib/pure/unicode.nim207
-rw-r--r--lib/pure/unittest.nim4
-rw-r--r--lib/pure/uri.nim174
-rw-r--r--lib/std/sha1.nim65
-rw-r--r--lib/std/time_t.nim23
-rw-r--r--lib/std/wordwrap.nim5
-rw-r--r--lib/system.nim205
-rw-r--r--lib/system/alloc.nim6
-rw-r--r--lib/system/ansi_c.nim2
-rw-r--r--lib/system/embedded.nim1
-rw-r--r--lib/system/excpt.nim5
-rw-r--r--lib/system/gc.nim18
-rw-r--r--lib/system/gc_common.nim2
-rw-r--r--lib/system/gc_ms.nim3
-rw-r--r--lib/system/gc_regions.nim29
-rw-r--r--lib/system/helpers.nim23
-rw-r--r--lib/system/helpers2.nim2
-rw-r--r--lib/system/jssys.nim2
-rw-r--r--lib/system/mmdisp.nim3
-rw-r--r--lib/system/nimscript.nim35
-rw-r--r--lib/system/profiler.nim14
-rw-r--r--lib/system/strmantle.nim2
-rw-r--r--lib/system/sysio.nim17
-rw-r--r--lib/system/timers.nim2
-rw-r--r--lib/windows/winlean.nim11
-rw-r--r--lib/wrappers/openssl.nim6
-rw-r--r--nimdoc/tester.nim2
-rw-r--r--nimdoc/testproject/expected/subdir/subdir_b/utils.html21
-rw-r--r--nimdoc/testproject/expected/testproject.html2
-rw-r--r--nimdoc/testproject/expected/theindex.html2
-rw-r--r--nimdoc/testproject/subdir/subdir_b/utils.nim24
-rw-r--r--nimpretty/nimpretty.nim24
-rw-r--r--nimsuggest/nimsuggest.nim21
-rw-r--r--nimsuggest/tests/taccent_highlight.nim8
-rw-r--r--nimsuggest/tests/ttype_highlight.nim28
-rw-r--r--readme.md2
-rw-r--r--testament/categories.nim116
-rw-r--r--testament/htmlgen.nim9
-rw-r--r--testament/lib/stdtest/specialpaths.nim31
-rw-r--r--testament/specs.nim2
-rw-r--r--testament/testamenthtml.nimf (renamed from testament/testamenthtml.templ)0
-rw-r--r--testament/tester.nim103
-rw-r--r--tests/array/tarray.nim2
-rw-r--r--tests/async/tasyncfilewrite.nim3
-rw-r--r--tests/async/tasynctry.nim1
-rw-r--r--tests/async/twinasyncrw.nim2
-rw-r--r--tests/bind/tnicerrorforsymchoice.nim9
-rw-r--r--tests/ccgbugs/t5701.nim6
-rw-r--r--tests/ccgbugs/tforward_decl_only.nim2
-rw-r--r--tests/collections/tcollections_to_string.nim6
-rw-r--r--tests/compiler/nim.cfg7
-rw-r--r--tests/compiler/tasciitables.nim109
-rw-r--r--tests/compiler/tunittest_light.nim55
-rw-r--r--tests/cpp/t10148.nim29
-rw-r--r--tests/cpp/t10241.nim19
-rw-r--r--tests/deprecated/tannot.nim9
-rw-r--r--tests/deprecated/tdeprecated.nim18
-rw-r--r--tests/destructor/helper.nim3
-rw-r--r--tests/destructor/tdestructor.nim71
-rw-r--r--tests/destructor/terror_module.nim20
-rw-r--r--tests/discard/tneedsdiscard.nim2
-rw-r--r--tests/enum/tenumfieldpragma.nim22
-rw-r--r--tests/enum/tenumfieldpragmanoannot.nim (renamed from tests/deprecated/tnoannot.nim)9
-rw-r--r--tests/errmsgs/m8794.nim2
-rw-r--r--tests/errmsgs/t8794.nim39
-rw-r--r--tests/errmsgs/t9768.nim30
-rw-r--r--tests/errmsgs/tinteger_literals.nim15
-rw-r--r--tests/errmsgs/tnested_generic_instantiation.nim6
-rw-r--r--tests/errmsgs/tnested_generic_instantiation2.nim27
-rw-r--r--tests/errmsgs/treportunused.nim8
-rw-r--r--tests/errmsgs/tunknown_named_parameter.nim8
-rw-r--r--tests/exception/t9657.nim2
-rw-r--r--tests/exception/tdefer1.nim10
-rw-r--r--tests/float/tfloatnan.nim28
-rw-r--r--tests/gc/gcbench.nim68
-rw-r--r--tests/gc/gcleak.nim2
-rw-r--r--tests/gc/thavlak.nim27
-rw-r--r--tests/gc/tlists.nim10
-rw-r--r--tests/generics/treentranttypes.nim2
-rw-r--r--tests/iter/titervaropenarray.nim1
-rw-r--r--tests/js/t9410.nim37
-rw-r--r--tests/js/tclosures.nim44
-rw-r--r--tests/js/test1.nim32
-rw-r--r--tests/js/test2.nim3
-rw-r--r--tests/js/testobjs.nim17
-rw-r--r--tests/js/tjsffi.nim16
-rw-r--r--tests/macros/tquotedo.nim11
-rw-r--r--tests/macros/tvarargsuntyped.nim30
-rw-r--r--tests/manyloc/keineschweine/enet_server/enet_server.nim2
-rw-r--r--tests/manyloc/keineschweine/server/old_dirserver.nim2
-rw-r--r--tests/manyloc/keineschweine/server/old_sg_server.nim2
-rw-r--r--tests/manyloc/nake/nakefile.nim7
-rw-r--r--tests/manyloc/standalone/barebone.nim6
-rw-r--r--tests/metatype/tmetatype_issues.nim3
-rw-r--r--tests/metatype/ttypetraits2.nim18
-rw-r--r--tests/method/tmapper.nim2
-rw-r--r--tests/misc/tparseopt.nim153
-rw-r--r--tests/misc/tsizeof.nim66
-rw-r--r--tests/modules/mimport_in_config.nim2
-rw-r--r--tests/modules/timport_in_config.nim9
-rw-r--r--tests/modules/timport_in_config.nim.cfg2
-rw-r--r--tests/modules/tmismatchedvisibility.nim2
-rw-r--r--tests/nim.cfg1
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl_test.nim24
-rw-r--r--tests/objects/t3734.nim17
-rw-r--r--tests/objects/tobjcov.nim4
-rw-r--r--tests/objects/tobject.nim20
-rw-r--r--tests/objvariant/tyaoption.nim25
-rw-r--r--tests/parallel/twrong_refcounts.nim3
-rw-r--r--tests/pragmas/t5149.nim12
-rw-r--r--tests/pragmas/tcustom_pragma.nim44
-rw-r--r--tests/pragmas/tused.nim2
-rw-r--r--tests/proc/tillegalreturntype.nim12
-rw-r--r--tests/proc/typed.nim7
-rw-r--r--tests/proc/untyped.nim7
-rw-r--r--tests/statictypes/tstatictypes.nim21
-rw-r--r--tests/stdlib/t10231.nim15
-rw-r--r--tests/stdlib/tgetaddrinfo.nim36
-rw-r--r--tests/stdlib/tjsonmacro.nim4
-rw-r--r--tests/stdlib/tmath.nim91
-rw-r--r--tests/stdlib/tmget.nim8
-rw-r--r--tests/stdlib/tos.nim110
-rw-r--r--tests/stdlib/tosproc.nim112
-rw-r--r--tests/stdlib/trepr.nim11
-rw-r--r--tests/stdlib/ttimes.nim11
-rw-r--r--tests/stdlib/tunittest.nim1
-rw-r--r--tests/system/tsystem_misc.nim37
-rw-r--r--tests/test_nimscript.nims1
-rw-r--r--tests/testament/tshouldfail.nim22
-rw-r--r--tests/threads/tactors.nim13
-rw-r--r--tests/threads/tactors2.nim25
-rw-r--r--tests/threads/trecursive_actor.nim20
-rw-r--r--tests/trmacros/trmacros_various2.nim10
-rw-r--r--tests/tuples/ttuples_various.nim16
-rw-r--r--tests/tuples/tuple_with_nil.nim1
-rw-r--r--tests/typerel/t4799.nim1
-rw-r--r--tests/typerel/t4799_1.nim1
-rw-r--r--tests/typerel/t4799_2.nim1
-rw-r--r--tests/typerel/t4799_3.nim1
-rw-r--r--tests/vm/tcompiletimesideeffects.nim42
-rw-r--r--tests/vm/tgorge.nim2
-rw-r--r--tests/vm/tissues.nim14
-rw-r--r--tests/vm/treset.nim28
-rw-r--r--tests/vm/tvmops.nim47
-rw-r--r--tools/kochdocs.nim14
-rw-r--r--tools/nimgrep.nim4
-rw-r--r--tools/vccexe/vccenv.nim4
285 files changed, 10589 insertions, 6716 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 383ec23f8..5fe7971a3 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,37 +1,32 @@
 ---
 name: Bug report
-about: You found an unexpected behaviour? Use this template.
-
+about: Have you found an unexpected behavior? Use this template.
 ---
 
 <!-- Think about the title, twice. -->
 
-<!-- Summarize the Problem here, keep it simple. -->
-<!-- e.g. `echo` outputs the wrong string. -->
+<!-- Summarize the problem here, keep it short and simple. -->
+Function `echo` outputs the wrong string.
 
 
 ### Example
-<!-- This should be a source code block. 
+<!-- Paste your example in the code-block below. -->
 ```nim
 echo "Hello World!"
 ```
--->
 
 
 ### Current Output
-<!--
 ```
 Hola mundo!
 ```
--->
 
 
 ### Expected Output
-<!--
+<!-- What should be the correct output? -->
 ```
 Hello World!
 ```
--->
 
 
 ### Possible Solution
@@ -40,6 +35,11 @@ Hello World!
 
 ### Additional Information
 <!--- For Example:
-   A link to a project where the issue is relevant.
-   A link to a related issue or discussion.
+* Your Nim version (output of `nim -v`).
+* Was it working in the previous Nim releases?
+* A link to a related issue or discussion.
 -->
+```
+$ nim -v
+Nim Compiler Version 0.1.2
+```
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 04da773bf..52aa53a67 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -1,9 +1,18 @@
 ---
 name: Feature request
-about: You want to suggest a new feature? Use this template.
+about: Do you want to suggest a new feature? Use this template.
 
 ---
 
+<!---
+Notice there is now a separate repository for the detailed RFCs and proposals:
+https://github.com/nim-lang/RFCs
+
+If you have a simple feature request, you can post it here using this template,
+but bear in mind that adding new features to the language is currently a low priority.
+-->
+
+
 ### Summary
 <!--- Short summary of your proposed feature -->
 
@@ -18,6 +27,6 @@ about: You want to suggest a new feature? Use this template.
 
 ### Additional Information
 <!--- For Example:
-   A link to a project where the issue is relevant.
-   A link to a related issue or discussion.
-   -->
+* A link to a project where the issue is relevant.
+* A link to a related issue or discussion.
+-->
diff --git a/.gitignore b/.gitignore
index cbfb98a1b..2bf0bf30c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,7 @@ doc/*.html
 doc/*.pdf
 doc/*.idx
 /web/upload
-build/*
+/build/*
 bin/*
 
 # iOS specific wildcards.
@@ -71,3 +71,7 @@ test.txt
 tweeter.db
 tweeter_test.db
 megatest.nim
+
+/outputExpected.txt
+/outputGotten.txt
+
diff --git a/.travis.yml b/.travis.yml
index abd115701..ea5d54ead 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,8 +14,9 @@ matrix:
     - os: osx
       env: NIM_COMPILE_TO_CPP=true
 
-  allow_failures:
-    - env: NIM_COMPILE_TO_CPP=true
+# To allow failures for a failing configuration, use something like:
+#  allow_failures:
+#    - env: NIM_COMPILE_TO_CPP=true
 #    - os: osx
 
 addons:
@@ -40,26 +41,11 @@ before_script:
   - sh build.sh
   - cd ..
   - export PATH=$(pwd)/bin${PATH:+:$PATH}
+  - echo PATH:${PATH}
+
 script:
   - nim c koch
-  - env NIM_COMPILE_TO_CPP=false ./koch boot
-  - ./koch boot -d:release
-  - ./koch nimble
-  - nim e tests/test_nimscript.nims
-  #- nimble install zip -y
-  #- nimble install opengl
-  #- nimble install sdl1
-  #- nimble install jester@#head -y
-  #- nimble install niminst
-  - nim c -d:nimCoroutines testament/tester
-  - testament/tester --pedantic all -d:nimCoroutines
-  - nim c -o:bin/nimpretty nimpretty/nimpretty.nim
-  - nim c -r nimpretty/tester.nim
-  - ./koch docs --git.commit:devel
-  - ./koch csource
-  - ./koch nimsuggest
-  - nim c -r nimsuggest/tester
-  - nim c -r nimdoc/tester
+  - ./koch runCI
 
 before_deploy:
   # Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html
diff --git a/appveyor.yml b/appveyor.yml
index 9c8525d72..8c4b2a07e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -44,22 +44,6 @@ install:
 
 build_script:
   - bin\nim c koch
-  - koch boot -d:release
-  - koch nimble
-  - nim e tests/test_nimscript.nims
-  - nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim
-  - nim c -r nimpretty/tester.nim
-#  - nimble install zip -y
-#  - nimble install opengl -y
-#  - nimble install sdl1 -y
-#  - nimble install jester@#head -y
-  - nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester
-  - nim c -d:nimCoroutines testament/tester
-
-test_script:
-  - testament\tester --pedantic all -d:nimCoroutines
-  - nim c -r nimdoc\tester
-#  - koch csource
-#  - koch zip
+  - koch runCI
 
 deploy: off
diff --git a/build_all.sh b/build_all.sh
index a268cb791..0706dcaf2 100644
--- a/build_all.sh
+++ b/build_all.sh
@@ -26,6 +26,9 @@ build_nim_csources(){
 
 [ -f $nim_csources ] || echo_run build_nim_csources
 
-echo_run bin/nim c koch
+# Note: if fails, may need to `cd csources && git pull`
+# see D20190115T162028
+echo_run bin/nim c --skipUserCfg --skipParentCfg koch
+
 echo_run ./koch boot -d:release
 echo_run ./koch tools # Compile Nimble and other tools.
diff --git a/changelog.md b/changelog.md
index e48f72023..d56d22506 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,5 @@
-## v0.20.0 - XX/XX/2018
+## v0.20.0 - XX/XX/2019
+
 
 ### Changes affecting backwards compatibility
 
@@ -23,13 +24,19 @@
 - The undocumented ``#? strongSpaces`` parsing mode has been removed.
 - The `not` operator is now always a unary operator, this means that code like
   ``assert not isFalse(3)`` compiles.
+- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs`
+  tree from the symbol declaration instead of just the initializer portion.
 
 
 #### Breaking changes in the standard library
 
 - `osproc.execProcess` now also takes a `workingDir` parameter.
 
-- `options.UnpackError` is no longer a ref type and inherits from `System.Defect` instead of `System.ValueError`.
+- `options.UnpackError` is no longer a ref type and inherits from `system.Defect` instead of `system.ValueError`.
+
+- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
+
+- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`, `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a `ValueError` when the parsed integer is outside of the valid range. Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
 
 - nre's `RegexMatch.{captureBounds,captures}[]`  no longer return `Option` or
   `nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
@@ -50,6 +57,18 @@
   `IndexError` for un-named captures. This is consistant with
   `RegexMatch.{captureBounds,captures}[]`.
 
+- splitFile now correctly handles edge cases, see #10047
+
+- `isNil` is no longer false for undefined in the JavaScript backend: now it's true for both nil and undefined. Use `isNull` or `isUndefined` if you need exact equallity: `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`.
+
+- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`,
+  `unsigned`, `actors`, `parseurl`
+
+- two poorly documented and not used modules (`subexes`, `scgi`) were moved to
+  graveyard (they are available as Nimble packages)
+
+
+
 #### Breaking changes in the compiler
 
 - The compiler now implements the "generic symbol prepass" for `when` statements
@@ -66,6 +85,9 @@ proc enumToString*(enums: openArray[enum]): string =
 ```
 
 - ``discard x`` is now illegal when `x` is a function symbol.
+- Implicit imports via ``--import: module`` in a config file are now restricted
+  to the main package.
+
 
 ### Library additions
 
@@ -87,11 +109,18 @@ proc enumToString*(enums: openArray[enum]): string =
   is instantiation of generic proc symbol.
 
 - Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
+
 - There is a new stdlib module `std/diff` to compute the famous "diff"
   of two texts by line.
 
 - Added `os.relativePath`.
 
+- Added `parseopt.remainingArgs`.
+
+- Added `os.getCurrentCompilerExe` (implmented as `getAppFilename` at CT),
+  can be used to retrieve the currently executing compiler.
+
+
 ### Library changes
 
 - The string output of `macros.lispRepr` proc has been tweaked
@@ -111,16 +140,23 @@ proc enumToString*(enums: openArray[enum]): string =
   (default value: true) that can be set to `false` for better Posix
   interoperability. (Bug #9619.)
 
+- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
+  differently.
+
+- `securehash` is moved to `lib/deprecated`
+
 
 ### Language additions
 
-- Vm suport for float32<->int32 and float64<->int64 casts was added.
+- Vm support for float32<->int32 and float64<->int64 casts was added.
 - There is a new pragma block `noSideEffect` that works like
   the `gcsafe` pragma block.
 - added os.getCurrentProcessId()
 - User defined pragmas are now allowed in the pragma blocks
-- Pragma blocks are now longer eliminated from the typed AST tree to preserve
+- Pragma blocks are no longer eliminated from the typed AST tree to preserve
   pragmas for further analysis by macros
+- Custom pragmas are now supported for `var` and `let` symbols.
+
 
 ### Language changes
 
@@ -129,13 +165,23 @@ proc enumToString*(enums: openArray[enum]): string =
   it's more recognizable and allows tools like github to recognize it as Nim,
   see [#9647](https://github.com/nim-lang/Nim/issues/9647).
   The previous extension will continue to work.
+- Pragma syntax is now consistent. Previous syntax where type pragmas did not
+  follow the type name is now deprecated. Also pragma before generic parameter
+  list is deprecated to be consistent with how pragmas are used with a proc. See
+  [#8514](https://github.com/nim-lang/Nim/issues/8514) and
+  [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
+
 
 ### Tool changes
 - `jsondoc` now include a `moduleDescription` field with the module
   description. `jsondoc0` shows comments as it's own objects as shown in the
   documentation.
+- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
+  un-documented; use `git` instead of relying on backup files.
+
 
 ### Compiler changes
 - The deprecated `fmod` proc is now unavailable on the VM'.
 
+
 ### Bugfixes
diff --git a/compiler.nimble b/compiler.nimble
index 5929df3ec..f4da45519 100644
--- a/compiler.nimble
+++ b/compiler.nimble
@@ -4,6 +4,6 @@ author = "Andreas Rumpf"
 description = "Compiler package providing the compiler sources as a library."
 license = "MIT"
 
-installDirs = @["compiler"]
+installDirs = @["compiler", "nimsuggest"]
 
 requires "nim >= 0.14.0"
diff --git a/compiler/asciitables.nim b/compiler/asciitables.nim
new file mode 100644
index 000000000..c25d54bde
--- /dev/null
+++ b/compiler/asciitables.nim
@@ -0,0 +1,83 @@
+#[
+move to std/asciitables.nim once stable, or to a nimble paackage
+once compiler can depend on nimble
+]#
+
+type Cell* = object
+  text*: string
+  width*, row*, col*, ncols*, nrows*: int
+
+iterator parseTableCells*(s: string, delim = '\t'): Cell =
+  ## iterates over all cells in a `delim`-delimited `s`, after a 1st
+  ## pass that computes number of rows, columns, and width of each column.
+  var widths: seq[int]
+  var cell: Cell
+  template update() =
+    if widths.len<=cell.col:
+      widths.setLen cell.col+1
+      widths[cell.col] = cell.width
+    else:
+      widths[cell.col] = max(widths[cell.col], cell.width)
+    cell.width = 0
+
+  for a in s:
+    case a
+    of '\n':
+      update()
+      cell.col = 0
+      cell.row.inc
+    elif a == delim:
+      update()
+      cell.col.inc
+    else:
+      # todo: consider multi-width chars when porting to non-ascii implementation
+      cell.width.inc
+  if s.len > 0 and s[^1] != '\n':
+    update()
+
+  cell.ncols = widths.len
+  cell.nrows = cell.row + 1
+  cell.row = 0
+  cell.col = 0
+  cell.width = 0
+
+  template update2() =
+    cell.width = widths[cell.col]
+    yield cell
+    cell.text = ""
+    cell.width = 0
+    cell.col.inc
+
+  template finishRow() =
+    for col in cell.col..<cell.ncols:
+      cell.col = col
+      update2()
+    cell.col = 0
+
+  for a in s:
+    case a
+    of '\n':
+      finishRow()
+      cell.row.inc
+    elif a == delim:
+      update2()
+    else:
+      cell.width.inc
+      cell.text.add a
+
+  if s.len > 0 and s[^1] != '\n':
+    finishRow()
+
+proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string =
+  ## formats a `delim`-delimited `s` representing a table; each cell is aligned
+  ## to a width that's computed for each column; consecutive columns are
+  ## delimted by `sep`, and alignment space is filled using `fill`.
+  ## More customized formatting can be done by calling `parseTableCells` directly.
+  for cell in parseTableCells(s, delim):
+    result.add cell.text
+    for i in cell.text.len..<cell.width:
+      result.add fill
+    if cell.col < cell.ncols-1:
+      result.add sep
+    if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
+      result.add '\n'
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 5f5f296cb..24891d6d3 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1087,6 +1087,13 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   when debugIds:
     registerId(result)
 
+proc astdef*(s: PSym): PNode =
+  # get only the definition (initializer) portion of the ast
+  if s.ast != nil and s.ast.kind == nkIdentDefs:
+    s.ast[2]
+  else:
+    s.ast
+
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil) or
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index d00371dd8..ed6255004 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1501,7 +1501,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
                 addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
   of tyOpenArray, tyVarargs:
     var b: TLoc
-    case a.t.kind
+    case skipTypes(a.t, abstractVarRange).kind
     of tyOpenArray, tyVarargs:
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 144a45816..2d68a198e 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -31,6 +31,7 @@ const
     cfsData: "NIM_merge_DATA",
     cfsProcs: "NIM_merge_PROCS",
     cfsInitProc: "NIM_merge_INIT_PROC",
+    cfsDatInitProc: "NIM_merge_DATINIT_PROC",    
     cfsTypeInit1: "NIM_merge_TYPE_INIT1",
     cfsTypeInit2: "NIM_merge_TYPE_INIT2",
     cfsTypeInit3: "NIM_merge_TYPE_INIT3",
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 266f63647..23e16cb93 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -436,30 +436,25 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
   if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
 
 proc genRecordFieldsAux(m: BModule, n: PNode,
-                        accessExpr: Rope, rectype: PType,
+                        rectype: PType,
                         check: var IntSet): Rope =
   result = nil
   case n.kind
   of nkRecList:
     for i in countup(0, sonsLen(n) - 1):
-      add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
+      add(result, genRecordFieldsAux(m, n.sons[i], rectype, check))
   of nkRecCase:
     if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
-    add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
+    add(result, genRecordFieldsAux(m, n.sons[0], rectype, check))
     # prefix mangled name with "_U" to avoid clashes with other field names,
     # since identifiers are not allowed to start with '_'
-    let uname = rope("_U" & mangle(n.sons[0].sym.name.s))
-    let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname]
-             else: uname
     var unionBody: Rope = nil
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
         let k = lastSon(n.sons[i])
         if k.kind != nkSym:
-          let sname = "S" & rope(i)
-          let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
-                                     check)
+          let a = genRecordFieldsAux(m, k, rectype, check)
           if a != nil:
             if tfPacked notin rectype.flags:
               add(unionBody, "struct {")
@@ -469,22 +464,20 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
               else:
                 addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
             add(unionBody, a)
-            addf(unionBody, "} $1;$n", [sname])
+            addf(unionBody, "};$n", [])
             if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
               addf(unionBody, "#pragma pack(pop)$n", [])
         else:
-          add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
+          add(unionBody, genRecordFieldsAux(m, k, rectype, check))
       else: internalError(m.config, "genRecordFieldsAux(record case branch)")
     if unionBody != nil:
-      addf(result, "union{$n$1} $2;$n", [unionBody, uname])
+      addf(result, "union{$n$1};$n", [unionBody])
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
     let sname = mangleRecFieldName(m, field)
-    let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
-             else: sname
-    fillLoc(field.loc, locField, n, ae, OnUnknown)
+    fillLoc(field.loc, locField, n, sname, OnUnknown)
     # for importcpp'ed objects, we only need to set field.loc, but don't
     # have to recurse via 'getTypeDescAux'. And not doing so prevents problems
     # with heavily templatized C++ code:
@@ -505,7 +498,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
   else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
 proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
-  result = genRecordFieldsAux(m, typ.n, nil, typ, check)
+  result = genRecordFieldsAux(m, typ.n, typ, check)
 
 proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
@@ -544,12 +537,18 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
         appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
         if typ.sym.magic == mException:
           # Add cleanup destructor to Exception base class
-          appcg(m, result, "~$1() {if(this->raiseId) popCurrentExceptionEx(this->raiseId);}$n", [name])
+          appcg(m, result, "~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
           # hack: forward declare popCurrentExceptionEx() on top of type description,
           # proper request to generate popCurrentExceptionEx not possible for 2 reasons:
           # generated function will be below declared Exception type and circular dependency
           # between Exception and popCurrentExceptionEx function
-          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result
+
+          let popExSym = magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")
+          if lfDynamicLib in popExSym.loc.flags and sfImportc in popExSym.flags:
+            #  echo popExSym.flags, " ma flags ", popExSym.loc.flags
+            result = "extern " & getTypeDescAux(m, popExSym.typ, check) & " " & mangleName(m, popExSym) & ";\L" & result
+          else:
+            result = genProcHeader(m, popExSym) & ";\L" & result
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
@@ -971,9 +970,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
 
 proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
   # bugfix: we need to search the type that contains the discriminator:
-  var objtype = objtype
+  var objtype = objtype.skipTypes(abstractPtrs)
   while lookupInRecord(objtype.n, d.name) == nil:
-    objtype = objtype.sons[0]
+    objtype = objtype.sons[0].skipTypes(abstractPtrs)
   if objtype.sym == nil:
     internalError(m.config, d.info, "anonymous obj with discriminator")
   result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
@@ -1042,6 +1041,8 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
       else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
   of nkSym:
     var field = n.sym
+    # Do not produce code for void types
+    if isEmptyType(field.typ): return
     if field.bitsize == 0:
       if field.loc.r == nil: fillObjectFields(m, typ)
       if field.loc.t == nil:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 457a6e176..e4f16f4ed 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
   result = getCopyright(conf, cfile)
   addIntTypes(result, conf)
 
-proc genFilenames(m: BModule): Rope =
-  discard cgsym(m, "dbgRegisterFilename")
-  result = nil
-  for i in 0..<m.config.m.fileInfos.len:
-    result.addf("dbgRegisterFilename($1);$N",
-      [m.config.m.fileInfos[i].projPath.string.makeCString])
-
 proc genMainProc(m: BModule) =
+  ## this function is called in cgenWriteModules after all modules are closed,
+  ## it means raising dependency on the symbols is too late as it will not propogate
+  ## into other modules, only simple rope manipulations are allowed
+
   const
     # The use of a volatile function pointer to call Pre/NimMainInner
     # prevents inlining of the NimMainInner function and dependent
     # functions, which might otherwise merge their stack frames.
     PreMainBody =
       "void PreMainInner(void) {$N" &
-      "\tsystemInit000();$N" &
-      "$1" &
       "$2" &
       "$3" &
       "}$N$N" &
       "void PreMain(void) {$N" &
       "\tvoid (*volatile inner)(void);$N" &
-      "\tsystemDatInit000();$N" &
       "\tinner = PreMainInner;$N" &
-      "$4$5" &
+      "$1" &
       "\t(*inner)();$N" &
       "}$N$N"
 
@@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) =
   else:
     nimMain = PosixNimMain
     otherMain = PosixCMain
-  if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
   if optEndb in m.config.options:
-    m.g.breakpoints.add(m.genFilenames)
+    for i in 0..<m.config.m.fileInfos.len:
+      m.g.breakpoints.addf("dbgRegisterFilename($1);$N",
+        [m.config.m.fileInfos[i].projPath.string.makeCString])
 
   let initStackBottomCall =
     if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
   inc(m.labels)
   appcg(m, m.s[cfsProcs], PreMainBody, [
-    m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
-     if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
-       ropecg(m, "\t#initThreadVarsEmulation();$N")
-     else:
-       "".rope,
-     initStackBottomCall])
+    m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit])
 
   appcg(m, m.s[cfsProcs], nimMain,
         [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
@@ -1260,22 +1250,61 @@ proc getInitName(m: PSym): Rope =
 
 proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
 
-proc registerModuleToMain(g: BModuleList; m: PSym) =
-  var
-    init = m.getInitName
-    datInit = m.getDatInitName
-  addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
-  addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
-  if sfSystemModule notin m.flags:
+
+proc registerModuleToMain(g: BModuleList; m: BModule) =
+  if m.s[cfsDatInitProc].len > 0:
+    let datInit = m.module.getDatInitName
+    addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
     addf(g.mainDatInit, "\t$1();$N", [datInit])
+
+  # Initialization of TLS and GC should be done in between
+  # systemDatInit and systemInit calls if any
+  if sfSystemModule in m.module.flags:
+    if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
+      add(g.mainDatInit, ropecg(m, "\t#initThreadVarsEmulation();$N"))
+    if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
+      add(g.mainDatInit, ropecg(m, "\t#initStackBottomWith((void *)&inner);$N"))
+
+  if m.s[cfsInitProc].len > 0:
+    let init = m.module.getInitName
+    addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
     let initCall = "\t$1();$N" % [init]
-    if sfMainModule in m.flags:
+    if sfMainModule in m.module.flags:
       add(g.mainModInit, initCall)
+    elif sfSystemModule in m.module.flags:
+      add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any
     else:
       add(g.otherModsInit, initCall)
 
+proc genDatInitCode(m: BModule) =
+  ## this function is called in cgenWriteModules after all modules are closed,
+  ## it means raising dependency on the symbols is too late as it will not propogate
+  ## into other modules, only simple rope manipulations are allowed
+
+  var moduleDatInitRequired = false
+
+  var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" %
+                 [getDatInitName(m.module)]
+
+  for i in cfsTypeInit1..cfsDynLibInit:
+    if m.s[i].len != 0:
+      moduleDatInitRequired = true
+      add(prc, genSectionStart(i, m.config))
+      add(prc, m.s[i])
+      add(prc, genSectionEnd(i, m.config))
+
+  addf(prc, "}$N$N", [])
+
+  if moduleDatInitRequired:
+    add(m.s[cfsDatInitProc], prc)
+
 proc genInitCode(m: BModule) =
-  var initname = getInitName(m.module)
+  ## this function is called in cgenWriteModules after all modules are closed,
+  ## it means raising dependency on the symbols is too late as it will not propogate
+  ## into other modules, only simple rope manipulations are allowed
+
+  var moduleInitRequired = false
+  let initname = getInitName(m.module)
   var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
   if m.typeNodes > 0:
     appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
@@ -1290,82 +1319,115 @@ proc genInitCode(m: BModule) =
     # Keep a bogus frame in case the code needs one
     add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
 
+    if m.preInitProc.s(cpsLocals).len > 0:
+      moduleInitRequired = true
+      add(prc, genSectionStart(cpsLocals, m.config))
+      add(prc, m.preInitProc.s(cpsLocals))
+      add(prc, genSectionEnd(cpsLocals, m.config))
+
+    if m.preInitProc.s(cpsInit).len > 0:
+      moduleInitRequired = true
+      add(prc, genSectionStart(cpsInit, m.config))
+      add(prc, m.preInitProc.s(cpsInit))
+      add(prc, genSectionEnd(cpsInit, m.config))
+
+    if m.preInitProc.s(cpsStmts).len > 0:
+      moduleInitRequired = true
+      add(prc, genSectionStart(cpsStmts, m.config))
+      add(prc, m.preInitProc.s(cpsStmts))
+      add(prc, genSectionEnd(cpsStmts, m.config))
+  addf(prc, "}$N", [])
+
+  if m.initProc.gcFrameId > 0:
+    moduleInitRequired = true
+    add(prc, initGCFrame(m.initProc))
+
+  if m.initProc.s(cpsLocals).len > 0:
+    moduleInitRequired = true
     add(prc, genSectionStart(cpsLocals, m.config))
-    add(prc, m.preInitProc.s(cpsLocals))
+    add(prc, m.initProc.s(cpsLocals))
     add(prc, genSectionEnd(cpsLocals, m.config))
 
+  if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
+    moduleInitRequired = true
+    if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
+      # BUT: the generated init code might depend on a current frame, so
+      # declare it nevertheless:
+      incl m.flags, frameDeclared
+      if preventStackTrace notin m.flags:
+        var procname = makeCString(m.module.name.s)
+        add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
+      else:
+        add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
+
     add(prc, genSectionStart(cpsInit, m.config))
-    add(prc, m.preInitProc.s(cpsInit))
+    add(prc, m.initProc.s(cpsInit))
     add(prc, genSectionEnd(cpsInit, m.config))
 
     add(prc, genSectionStart(cpsStmts, m.config))
-    add(prc, m.preInitProc.s(cpsStmts))
+    add(prc, m.initProc.s(cpsStmts))
     add(prc, genSectionEnd(cpsStmts, m.config))
-  addf(prc, "}$N", [])
-
-  add(prc, initGCFrame(m.initProc))
 
-  add(prc, genSectionStart(cpsLocals, m.config))
-  add(prc, m.initProc.s(cpsLocals))
-  add(prc, genSectionEnd(cpsLocals, m.config))
+    if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
+      add(prc, deinitFrame(m.initProc))
 
-  if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
-    # BUT: the generated init code might depend on a current frame, so
-    # declare it nevertheless:
-    incl m.flags, frameDeclared
-    if preventStackTrace notin m.flags:
-      var procname = makeCString(m.module.name.s)
-      add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
-    else:
-      add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
+  if m.initProc.gcFrameId > 0:
+    moduleInitRequired = true
+    add(prc, deinitGCFrame(m.initProc))
 
-  add(prc, genSectionStart(cpsInit, m.config))
-  add(prc, m.initProc.s(cpsInit))
-  add(prc, genSectionEnd(cpsInit, m.config))
-
-  add(prc, genSectionStart(cpsStmts, m.config))
-  add(prc, m.initProc.s(cpsStmts))
-  add(prc, genSectionEnd(cpsStmts, m.config))
-
-  if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
-    add(prc, deinitFrame(m.initProc))
-  add(prc, deinitGCFrame(m.initProc))
   addf(prc, "}$N$N", [])
 
-  prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
-           [getDatInitName(m.module)])
-
-  for i in cfsTypeInit1..cfsDynLibInit:
-    add(prc, genSectionStart(i, m.config))
-    add(prc, m.s[i])
-    add(prc, genSectionEnd(i, m.config))
-
-  addf(prc, "}$N$N", [])
   # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
   # that would lead to a *nesting* of merge sections which the merger does
   # not support. So we add it to another special section: ``cfsInitProc``
-  add(m.s[cfsInitProc], prc)
 
   for i, el in pairs(m.extensionLoaders):
     if el != nil:
       let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
         [(i.ord - '0'.ord).rope, el]
-      add(m.s[cfsInitProc], ex)
+      moduleInitRequired = true
+      add(prc, ex)
+
+  if moduleInitRequired or sfMainModule in m.module.flags:
+    add(m.s[cfsInitProc], prc)
+
+  genDatInitCode(m)
+  registerModuleToMain(m.g, m)
 
 proc genModule(m: BModule, cfile: Cfile): Rope =
+  var moduleIsEmpty = true
+
   result = getFileHeader(m.config, cfile)
   result.add(genMergeInfo(m))
 
+  if m.config.cppCustomNamespace.len > 0:
+    result.add openNamespaceNim(m.config.cppCustomNamespace)
+
   generateThreadLocalStorage(m)
   generateHeaders(m)
-  for i in countup(cfsHeaders, cfsProcs):
-    add(result, genSectionStart(i, m.config))
-    add(result, m.s[i])
-    add(result, genSectionEnd(i, m.config))
-    if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
-      result.add openNamespaceNim(m.config.cppCustomNamespace)
-  add(result, m.s[cfsInitProc])
-  if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
+  add(result, genSectionStart(cfsHeaders, m.config))
+  add(result, m.s[cfsHeaders])
+  add(result, genSectionEnd(cfsHeaders, m.config))
+
+  for i in countup(cfsForwardTypes, cfsProcs):
+    if m.s[i].len > 0:
+      moduleIsEmpty = false
+      add(result, genSectionStart(i, m.config))
+      add(result, m.s[i])
+      add(result, genSectionEnd(i, m.config))
+
+  if m.s[cfsInitProc].len > 0:
+    moduleIsEmpty = false
+    add(result, m.s[cfsInitProc])
+  if m.s[cfsDatInitProc].len > 0:
+    moduleIsEmpty = false
+    add(result, m.s[cfsDatInitProc])
+
+  if m.config.cppCustomNamespace.len > 0:
+    result.add closeNamespaceNim()
+
+  if moduleIsEmpty:
+    result = nil
 
 proc newPreInitProc(m: BModule): BProc =
   result = newProc(nil, m)
@@ -1522,27 +1584,30 @@ proc writeModule(m: BModule, pending: bool) =
     finishTypeDescriptions(m)
     if sfMainModule in m.module.flags:
       # generate main file:
+      genMainProc(m)
       add(m.s[cfsProcHeaders], m.g.mainModProcs)
       generateThreadVarsSize(m)
 
     var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
     var code = genModule(m, cf)
-    when hasTinyCBackend:
-      if conf.cmd == cmdRun:
-        tccgen.compileCCode($code)
-        return
-
-    if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
-    addFileToCompile(m.config, cf)
+    if code != nil:
+      when hasTinyCBackend:
+        if conf.cmd == cmdRun:
+          tccgen.compileCCode($code)
+          return
+
+      if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
+      addFileToCompile(m.config, cf)
   elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
     let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
     mergeFiles(cfile, m)
     genInitCode(m)
     finishTypeDescriptions(m)
     var code = genModule(m, cf)
-    if not writeRope(code, cfile):
-      rawMessage(m.config, errCannotOpenFile, cfile.string)
-    addFileToCompile(m.config, cf)
+    if code != nil:
+      if not writeRope(code, cfile):
+        rawMessage(m.config, errCannotOpenFile, cfile.string)
+      addFileToCompile(m.config, cf)
   else:
     # Consider: first compilation compiles ``system.nim`` and produces
     # ``system.c`` but then compilation fails due to an error. This means
@@ -1560,13 +1625,16 @@ proc updateCachedModule(m: BModule) =
     mergeFiles(cfile, m)
     genInitCode(m)
     finishTypeDescriptions(m)
-
     var code = genModule(m, cf)
-    if not writeRope(code, cfile):
-      rawMessage(m.config, errCannotOpenFile, cfile.string)
+    if code != nil:
+      if not writeRope(code, cfile):
+        rawMessage(m.config, errCannotOpenFile, cfile.string)
+      addFileToCompile(m.config, cf)
   else:
+    if sfMainModule notin m.module.flags:
+      genMainProc(m)
     cf.flags = {CfileFlag.Cached}
-  addFileToCompile(m.config, cf)
+    addFileToCompile(m.config, cf)
 
 proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   result = n
@@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   if n != nil:
     m.initProc.options = initProcOptions(m)
     genStmts(m.initProc, n)
-  # cached modules need to registered too:
-  registerModuleToMain(m.g, m.module)
 
   if sfMainModule in m.module.flags:
+    # raise dependencies on behalf of genMainProc
+    if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
+      discard cgsym(m, "initStackBottomWith")
+    if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
+      discard cgsym(m, "initThreadVarsEmulation")
+
+    if m.g.breakpoints != nil:
+      discard cgsym(m, "dbgRegisterBreakpoint")
+    if optEndb in m.config.options:
+      discard cgsym(m, "dbgRegisterFilename")
+
     if m.g.forwardedProcs.len == 0:
       incl m.flags, objHasKidsValid
     let disp = generateMethodDispatchers(graph)
     for x in disp: genProcAux(m, x.sym)
-    genMainProc(m)
+
+  m.g.modules_closed.add m
 
 proc genForwardedProcs(g: BModuleList) =
   # Forward declared proc:s lack bodies when first encountered, so they're given
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 28e36364e..50b484e31 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -32,6 +32,7 @@ type
     cfsData,                  # section for C constant data
     cfsProcs,                 # section for C procs that are not inline
     cfsInitProc,              # section for the C init proc
+    cfsDatInitProc,           # section for the C datInit proc
     cfsTypeInit1,             # section 1 for declarations of type information
     cfsTypeInit2,             # section 2 for init of type information
     cfsTypeInit3,             # section 3 for init of type information
@@ -112,6 +113,7 @@ type
     mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
     mapping*: Rope             # the generated mapping file (if requested)
     modules*: seq[BModule]     # list of all compiled modules
+    modules_closed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
     forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
     generatedHeader*: BModule
     breakPointId*: int
@@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
     graph: g, nimtvDeclared: initIntSet())
 
 iterator cgenModules*(g: BModuleList): BModule =
-  for m in g.modules:
-    # ultimately, we are iterating over the file ids here.
-    # some "files" won't have an associated cgen module (like stdin)
-    # and we must skip over them.
-    if m != nil: yield m
+  for m in g.modules_closed:
+    # iterate modules in the order they were closed
+    yield m
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
index 9fbf4a0b0..e1824316a 100644
--- a/compiler/cmdlinehelper.nim
+++ b/compiler/cmdlinehelper.nim
@@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
   if self.suggestMode:
     conf.command = "nimsuggest"
 
-  # These defines/options should not be enabled while processing nimscript
-  # bug #4446, #9420, #8991, #9589, #9153
-  undefSymbol(conf.symbols, "profiler")
-  undefSymbol(conf.symbols, "memProfiler")
-  undefSymbol(conf.symbols, "nodejs")
-
-  # bug #9120
-  conf.globalOptions.excl(optTaintMode)
-
-  proc runNimScriptIfExists(path: AbsoluteFile)=
-    if fileExists(path):
-      runNimScript(cache, path, freshDefines = false, conf)
+  template runNimScriptIfExists(path: AbsoluteFile) =
+    let p = path # eval once
+    if fileExists(p):
+      runNimScript(cache, p, freshDefines = false, conf)
 
   # Caution: make sure this stays in sync with `loadConfigs`
   if optSkipSystemConfigFile notin conf.globalOptions:
@@ -88,9 +80,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
         # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
         discard
 
-  # Reload configuration from .cfg file
-  loadConfigs(DefaultConfig, cache, conf)
-
   # now process command line arguments again, because some options in the
   # command line can overwite the config file's settings
   extccomp.initVars(conf)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index b090a09a5..30521f9ca 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -142,7 +142,7 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC
 proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
                         info: TLineInfo) =
   case arg.normalize
-  of "on": conf.options = conf.options + op
+  of "","on": conf.options = conf.options + op
   of "off": conf.options = conf.options - op
   else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
@@ -158,7 +158,7 @@ proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass:
 proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
                          info: TLineInfo) =
   case arg.normalize
-  of "on": conf.globalOptions = conf.globalOptions + op
+  of "", "on": conf.globalOptions = conf.globalOptions + op
   of "off": conf.globalOptions = conf.globalOptions - op
   else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
 
@@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
     case arg.normalize
     of "boehm":        result = conf.selectedGC == gcBoehm
     of "refc":         result = conf.selectedGC == gcRefc
-    of "v2":           result = conf.selectedGC == gcV2
+    of "v2":           result = false
     of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
     of "generational": result = false
     of "destructors":  result = conf.selectedGC == gcDestructors
@@ -291,6 +291,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "patterns": result = contains(conf.options, optPatterns)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
   of "nilseqs": result = contains(conf.options, optNilSeqs)
+  of "oldast": result = contains(conf.options, optOldAst)
   else: invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@@ -413,26 +414,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if pass in {passCmd2, passPP}:
       addExternalFileToLink(conf, AbsoluteFile arg)
   of "debuginfo":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optCDebug)
+    processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
   of "embedsrc":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optEmbedOrigSrc)
+    processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
   of "compileonly", "c":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optCompileOnly)
+    processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
   of "nolinking":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optNoLinking)
+    processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
   of "nomain":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optNoMain)
+    processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
   of "forcebuild", "f":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optForceFullMake)
+    processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
   of "project":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optWholeProject
+    processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
   of "gc":
     expectArg(conf, switch, arg, pass, info)
     case arg.normalize
@@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     of "refc":
       conf.selectedGC = gcRefc
     of "v2":
-      conf.selectedGC = gcV2
+      message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
     of "markandsweep":
       conf.selectedGC = gcMarkAndSweep
       defineSymbol(conf.symbols, "gcmarkandsweep")
@@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     else: undefSymbol(conf.symbols, "hotcodereloading")
   of "oldnewlines":
     case arg.normalize
-    of "on":
+    of "","on":
       conf.oldNewlines = true
       defineSymbol(conf.symbols, "nimOldNewlines")
     of "off":
@@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       localError(conf, info, errOnOrOffExpectedButXFound % arg)
   of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
   of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
+  of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info)
   of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
   of "floatchecks":
     processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
@@ -590,16 +585,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
   of "import":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: conf.implicitImports.add arg
+    if pass in {passCmd2, passPP}:
+      conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
   of "include":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg
+    if pass in {passCmd2, passPP}:
+      conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
   of "listcmd":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optListCmd)
+    processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
   of "genmapping":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optGenMapping)
+    processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
   of "os":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
@@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       elif cpu != conf.target.hostCPU:
         setTarget(conf.target, conf.target.targetOS, cpu)
   of "run", "r":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optRun)
+    processOnOffSwitchG(conf, {optRun}, arg, pass, info)
   of "errormax":
     expectArg(conf, switch, arg, pass, info)
     # Note: `nim check` (etc) can overwrite this.
@@ -664,21 +658,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     of "v2": conf.symbolFiles = v2Sf
     else: localError(conf, info, "invalid option for --incremental: " & arg)
   of "skipcfg":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optSkipSystemConfigFile)
+    processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
   of "skipprojcfg":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optSkipProjConfigFile)
+    processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
   of "skipusercfg":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optSkipUserConfigFile)
+    processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
   of "skipparentcfg":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optSkipParentConfigFiles)
+    processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
   of "genscript", "gendeps":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optGenScript)
-    incl(conf.globalOptions, optCompileOnly)
+    processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
+    processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
   of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
   of "lib":
     expectArg(conf, switch, arg, pass, info)
@@ -712,16 +701,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectNoArg(conf, switch, arg, pass, info)
     conf.ideCmd = ideUse
   of "stdout":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl(conf.globalOptions, optStdout)
+    processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
   of "listfullpaths":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optListFullPaths
+    processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
   of "dynliboverride":
     dynlibOverride(conf, switch, arg, pass, info)
   of "dynliboverrideall":
-    expectNoArg(conf, switch, arg, pass, info)
-    incl conf.globalOptions, optDynlibOverrideAll
+    processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
   of "cs":
     # only supported for compatibility. Does nothing.
     expectArg(conf, switch, arg, pass, info)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 40af11e70..03ce9a5cf 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType =
 template genOp(opr, opname, ri) =
   let op = opr
   if op == nil:
-    globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
+    globalError(c.graph.config, dest.info, "internal error: '" & opname &
+      "' operator not found for type " & typeToString(t))
   elif op.ast[genericParamsPos].kind != nkEmpty:
     globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
   patchHead op
@@ -365,7 +366,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   result.add genWasMoved(n, c)
   result.add tempAsNode
 
-proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = 
+proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
   assert s.kind == nkSym and s.sym.kind == skParam
   if not isLastRead(s, c):
      localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
@@ -427,7 +428,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
       result = copyNode(arg)
       for i in 0..<arg.len:
         var branch = copyNode(arg[i])
-        if arg[i].kind in {nkElifBranch, nkElifExpr}:   
+        if arg[i].kind in {nkElifBranch, nkElifExpr}:
           branch.add p(arg[i][0], c)
           branch.add pArgIfTyped(arg[i][1])
         else:
@@ -442,13 +443,13 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
           branch = arg[i] # of branch conditions are constants
           branch[^1] = pArgIfTyped(arg[i][^1])
         elif arg[i].kind in {nkElifBranch, nkElifExpr}:
-          branch = copyNode(arg[i])   
+          branch = copyNode(arg[i])
           branch.add p(arg[i][0], c)
           branch.add pArgIfTyped(arg[i][1])
         else:
-          branch = copyNode(arg[i]) 
+          branch = copyNode(arg[i])
           branch.add pArgIfTyped(arg[i][0])
-        result.add branch     
+        result.add branch
     else:
       # an object that is not temporary but passed to a 'sink' parameter
       # results in a copy.
@@ -476,7 +477,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
     result.add ri2
   of nkBracketExpr:
     if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
-      # unpacking of tuple: move out the elements 
+      # unpacking of tuple: move out the elements
       result = genSink(c, dest.typ, dest, ri)
     else:
       result = genCopy(c, dest.typ, dest, ri)
@@ -509,11 +510,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
         branch = ri[i] # of branch conditions are constants
         branch[^1] = moveOrCopyIfTyped(ri[i][^1])
       elif ri[i].kind in {nkElifBranch, nkElifExpr}:
-        branch = copyNode(ri[i])   
+        branch = copyNode(ri[i])
         branch.add p(ri[i][0], c)
         branch.add moveOrCopyIfTyped(ri[i][1])
       else:
-        branch = copyNode(ri[i]) 
+        branch = copyNode(ri[i])
         branch.add moveOrCopyIfTyped(ri[i][0])
       result.add branch
   of nkBracket:
@@ -550,7 +551,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       sinkParamIsLastReadCheck(c, ri)
       var snk = genSink(c, dest.typ, dest, ri)
       snk.add ri
-      result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))   
+      result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
     elif ri.sym.kind != skParam and isLastRead(ri, c):
       # Rule 3: `=sink`(x, z); wasMoved(z)
       var snk = genSink(c, dest.typ, dest, ri)
@@ -647,7 +648,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
     let params = owner.typ.n
     for i in 1 ..< params.len:
       let param = params[i].sym
-      if param.typ.kind == tySink and hasDestructor(param.typ): 
+      if param.typ.kind == tySink and hasDestructor(param.typ):
         c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
 
   let body = p(n, c)
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index cd32d95d5..df9584576 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -86,7 +86,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
     result.add("\n")
     inc i
   if i in jumpTargets: result.add("L" & $i & ": End\n")
-
+  # consider calling `asciitables.alignTable`
 
 proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
   ## echos the ControlFlowGraph for debugging purposes.
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 67f4108e1..33cd98f38 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -40,6 +40,7 @@ type
                     # already. See bug #3655
     destFile*: AbsoluteFile
     thisDir*: AbsoluteDir
+    examples: string
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
@@ -118,7 +119,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
   result.conf = conf
   result.cache = cache
   initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
-                   conf.configVars, filename.string, {roSupportRawDirective},
+                   conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown},
                    docgenFindFile, compilerMsgHandler)
 
   if conf.configVars.hasKey("doc.googleAnalytics"):
@@ -378,6 +379,14 @@ proc testExample(d: PDoc; ex: PNode) =
       "_examples" & $d.exampleCounter & ".nim"))
   #let nimcache = outp.changeFileExt"" & "_nimcache"
   renderModule(ex, d.filename, outp.string, conf = d.conf)
+  d.examples.add "import r\"" & outp.string & "\"\n"
+
+proc runAllExamples(d: PDoc) =
+  if d.examples.len == 0: return
+  let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
+  let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
+      "_examples.nim"))
+  writeFile(outp, d.examples)
   let backend = if isDefined(d.conf, "js"): "js"
                 elif isDefined(d.conf, "cpp"): "cpp"
                 elif isDefined(d.conf, "objc"): "objc"
@@ -400,11 +409,9 @@ proc extractImports(n: PNode; result: PNode) =
   for i in 0..<n.safeLen: extractImports(n[i], result)
 
 proc prepareExamples(d: PDoc; n: PNode) =
-
   var docComment = newTree(nkCommentStmt)
   let loc = d.conf.toFileLineCol(n.info)
   docComment.comment = "autogenerated by docgen from " & loc
-
   var runnableExamples = newTree(nkStmtList,
       docComment,
       newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
@@ -935,6 +942,7 @@ proc generateIndex*(d: PDoc) =
     writeIndexFile(d[], dest.string)
 
 proc writeOutput*(d: PDoc, useWarning = false) =
+  runAllExamples(d)
   var content = genOutFile(d)
   if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
@@ -947,6 +955,7 @@ proc writeOutput*(d: PDoc, useWarning = false) =
         outfile.string)
 
 proc writeOutputJson*(d: PDoc, useWarning = false) =
+  runAllExamples(d)
   var modDesc: string
   for desc in d.modDesc:
     modDesc &= desc
@@ -982,7 +991,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
 
   d.isPureRst = true
   var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
-                     {roSupportRawDirective}, conf)
+                     {roSupportRawDirective, roSupportMarkdown}, conf)
   var modDesc = newStringOfCap(30_000)
   renderRstToOut(d[], rst, modDesc)
   d.modDesc = rope(modDesc)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 23f723e29..2c5af6433 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -63,8 +63,8 @@ compiler gcc:
   result = (
     name: "gcc",
     objExt: "o",
-    optSpeed: " -O3 -ffast-math ",
-    optSize: " -Os -ffast-math ",
+    optSpeed: " -O3 ",
+    optSize: " -Os ",
     compilerExe: "gcc",
     cppCompiler: "g++",
     compileTmpl: "-c $options $include -o $objfile $file",
@@ -88,8 +88,8 @@ compiler nintendoSwitchGCC:
   result = (
     name: "switch_gcc",
     objExt: "o",
-    optSpeed: " -O3 -ffast-math ",
-    optSize: " -Os -ffast-math ",
+    optSpeed: " -O3 ",
+    optSize: " -Os ",
     compilerExe: "aarch64-none-elf-gcc",
     cppCompiler: "aarch64-none-elf-g++",
     compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
@@ -153,6 +153,13 @@ compiler vcc:
     structStmtFmt: "$3$n$1 $2",
     props: {hasCpp, hasAssume, hasDeclspec})
 
+compiler clangcl:
+  result = vcc()
+  result.name = "clang_cl"
+  result.compilerExe = "clang-cl"
+  result.cppCompiler = "clang-cl"
+  result.linkerExe = "clang-cl"
+
 # Intel C/C++ Compiler
 compiler icl:
   result = vcc()
@@ -353,7 +360,8 @@ const
     pcc(),
     ucc(),
     icl(),
-    icc()]
+    icc(),
+    clangcl()]
 
   hExt* = ".h"
 
@@ -567,6 +575,8 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
     includeCmd = ""
     compilePattern = getCompilerExe(conf, c, cfile.cname)
 
+  includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
+
   var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
            else: cfile.cname
 
diff --git a/compiler/guards.nim b/compiler/guards.nim
index a01c023e4..46e18d3bf 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -257,9 +257,9 @@ proc canon*(n: PNode; o: Operators): PNode =
     for i in 0 ..< n.len:
       result.sons[i] = canon(n.sons[i], o)
   elif n.kind == nkSym and n.sym.kind == skLet and
-      n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
+      n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
       someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
-    result = n.sym.ast.copyTree
+    result = n.sym.astdef.copyTree
   else:
     result = n
   case result.getMagic
@@ -395,8 +395,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
     #   if a:
     #     ...
     # We make can easily replace 'a' by '2 < x' here:
-    if n.sym.ast != nil:
-      result = usefulFact(n.sym.ast, o)
+    if n.sym.astdef != nil:
+      result = usefulFact(n.sym.astdef, o)
   elif n.kind == nkStmtListExpr:
     result = usefulFact(n.lastSon, o)
 
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 131b1ad8a..eef0c9bb9 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
         rawImportSymbol(c, e)
         e = nextIdentIter(it, fromMod.tab)
     else: rawImportSymbol(c, s)
+  suggestSym(c.config, n.info, s, c.graph.usageSym, false)
 
 proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
   var i: TTabIter
@@ -161,9 +162,9 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
         localError(c.config, n.info, "A module cannot import itself")
     if sfDeprecated in result.flags:
       if result.constraint != nil:
-        message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
+        message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
       else:
-        message(c.config, n.info, warnDeprecated, result.name.s)
+        message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
     suggestSym(c.config, n.info, result, c.graph.usageSym, false)
     importStmtResult.add newSymNode(result, n.info)
     #newStrNode(toFullPath(c.config, f), n.info)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 4d22c4224..8625f2fe1 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -94,6 +94,7 @@ type
     options: TOptions
     module: BModule
     g: PGlobals
+    generatedParamCopies: IntSet
     beforeRetNeeded: bool
     unique: int    # for temp identifier generation
     blocks: seq[TBlock]
@@ -918,35 +919,15 @@ proc countJsParams(typ: PType): int =
 const
   nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
     nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
+    nkObjConstr, nkTupleConstr, nkBracket,
     nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
     nkCommand, nkHiddenCallConv, nkCallStrLit}
 
 proc needsNoCopy(p: PProc; y: PNode): bool =
-  # if the node is a literal object constructor we have to recursively
-  # check the expressions passed into it
-  case y.kind
-  of nkObjConstr:
-    for arg in y.sons[1..^1]:
-      if not needsNoCopy(p, arg[1]):
-        return false
-  of nkTupleConstr:
-    for arg in y.sons:
-      var arg = arg
-      if arg.kind == nkExprColonExpr:
-        arg = arg[1]
-      if not needsNoCopy(p, arg):
-        return false
-  of nkBracket:
-    for arg in y.sons:
-      if not needsNoCopy(p, arg):
-        return false
-  of nodeKindsNeedNoCopy:
-    return true
-  else:
-    return (mapType(y.typ) != etyBaseIndex and
-            (skipTypes(y.typ, abstractInst).kind in
-             {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
-  return true
+  return y.kind in nodeKindsNeedNoCopy or
+        ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
+          (skipTypes(y.typ, abstractInst).kind in
+            {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
 
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
   var a, b: TCompRes
@@ -969,7 +950,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
       lineF(p, "$1 = nimCopy(null, $2, $3);$n",
                [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
   of etyObject:
-    if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+    if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
@@ -1252,12 +1233,29 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) =
     if owner != nil: add(owner.locals, newp)
     else: attachProc(p, newp, s)
 
+proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
+  let s = n.sym
+  if p.prc == s.owner or needsNoCopy(p, n): 
+    return
+  var owner = p.up
+  while true:
+    if owner == nil:
+      internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
+    if owner.prc == s.owner:
+      if not owner.generatedParamCopies.containsOrIncl(s.id):
+        let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
+        add(owner.locals, owner.indentLine(copy))
+      return
+    owner = owner.up
+
 proc genSym(p: PProc, n: PNode, r: var TCompRes) =
   var s = n.sym
   case s.kind
   of skVar, skLet, skParam, skTemp, skResult, skForVar:
     if s.loc.r == nil:
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
+    if s.kind == skParam:
+      genCopyForParamIfNeeded(p, n)
     let k = mapType(p, s.typ)
     if k == etyBaseIndex:
       r.typ = etyBaseIndex
@@ -1504,6 +1502,8 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
     for i in countup(1, sonsLen(rec) - 1):
       createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
   of nkSym:
+    # Do not produce code for void types
+    if isEmptyType(rec.sym.typ): return
     if rec.sym.id notin excludedFieldIDs:
       if output.len > 0: output.add(", ")
       output.addf("$#: ", [mangleName(p.module, rec.sym)])
@@ -1886,12 +1886,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mLtStr:
     binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
   of mIsNil:
+    # we want to accept undefined, so we ==
     if mapType(n[1].typ) != etyBaseIndex:
-      unaryExpr(p, n, r, "", "($1 === null)")
+      unaryExpr(p, n, r, "", "($1 == null)")
     else:
       var x: TCompRes
       gen(p, n[1], x)
-      r.res = "($# === null && $# === 0)" % [x.address, x.res]
+      r.res = "($# == null && $# === 0)" % [x.address, x.res]
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
   of mChr: gen(p, n.sons[1], r)
@@ -1924,7 +1925,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
       else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
   of mSetLengthStr:
-    binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)")
+    binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)")
   of mSetLengthSeq:
     var x, y: TCompRes
     gen(p, n.sons[1], x)
@@ -2005,6 +2006,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
     if a.typ == etyBaseIndex:
       addf(r.res, "[$1, $2]", [a.address, a.res])
     else:
+      if not needsNoCopy(p, n[i]):
+        let typ = n[i].typ.skipTypes(abstractInst)
+        useMagic(p, "nimCopy")
+        a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
       add(r.res, a.res)
   add(r.res, "]")
 
@@ -2017,9 +2022,13 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
     var it = n.sons[i]
     if it.kind == nkExprColonExpr: it = it.sons[1]
     gen(p, it, a)
+    let typ = it.typ.skipTypes(abstractInst)
     if a.typ == etyBaseIndex:
       addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
     else:
+      if not needsNoCopy(p, it):
+        useMagic(p, "nimCopy")
+        a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
       addf(r.res, "Field$#: $#", [i.rope, a.res])
   r.res.add("}")
 
@@ -2039,17 +2048,12 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
     fieldIDs.incl(f.id)
 
     let typ = val.typ.skipTypes(abstractInst)
-    if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
-          mapType(p, typ) != etyBaseIndex) or
-          a.typ == etyBaseIndex or
-          needsNoCopy(p, it.sons[1]):
-      discard
-    else:
-      useMagic(p, "nimCopy")
-      a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
     if a.typ == etyBaseIndex:
       addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
     else:
+      if not needsNoCopy(p, val):
+        useMagic(p, "nimCopy")
+        a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
       addf(initList, "$#: $#", [f.loc.r, a.res])
   let t = skipTypes(n.typ, abstractInst + skipPtrs)
   createObjInitList(p, t, fieldIDs, initList)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 635e6f08d..a4414d186 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -17,7 +17,7 @@
 
 import
   hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg, lineinfos, pathutils
+  wordrecg, lineinfos, pathutils, parseutils
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) =
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
 
-{.push overflowChecks: off.}
-# We need to parse the largest uint literal without overflow checks
-proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
-  var i = start
-  if i < s.len and s[i] in {'0'..'9'}:
-    b = 0
-    while i < s.len and s[i] in {'0'..'9'}:
-      b = b * 10 + (ord(s[i]) - ord('0'))
-      inc(i)
-      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    result = i - start
-{.pop.} # overflowChecks
-
-
 template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
   add(t.literal, replacementChar)
   inc(L.bufpos)
@@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) =
       of floatTypes:
         result.fNumber = parseFloat(result.literal)
       of tkUint64Lit:
-        xi = 0
-        let len = unsafeParseUInt(result.literal, xi)
-        if len != result.literal.len or len == 0:
-          raise newException(ValueError, "invalid integer: " & $xi)
-        result.iNumber = xi
+        var iNumber: uint64
+        var len: int
+        try:
+          len = parseBiggestUInt(result.literal, iNumber)
+        except ValueError:
+          raise newException(OverflowError, "number out of range: " & $result.literal)
+        if len != result.literal.len:
+          raise newException(ValueError, "invalid integer: " & $result.literal)
+        result.iNumber = cast[int64](iNumber)
       else:
-        result.iNumber = parseBiggestInt(result.literal)
-
-      # Explicit bounds checks
+        var iNumber: int64
+        var len: int
+        try:
+          len = parseBiggestInt(result.literal, iNumber)
+        except ValueError:
+          raise newException(OverflowError, "number out of range: " & $result.literal)
+        if len != result.literal.len:
+          raise newException(ValueError, "invalid integer: " & $result.literal)
+        result.iNumber = iNumber
+
+      # Explicit bounds checks. Only T.high needs to be considered
+      # since result.iNumber can't be negative.
       let outOfRange =
         case result.tokType
-        of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
-        of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
-                        result.iNumber > BiggestInt(uint8.high))
-        of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
-        of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
-                        result.iNumber > BiggestInt(uint16.high))
-        of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
-        of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
-                        result.iNumber > BiggestInt(uint32.high))
+        of tkInt8Lit: result.iNumber > int8.high
+        of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high)
+        of tkInt16Lit: result.iNumber > int16.high
+        of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high)
+        of tkInt32Lit: result.iNumber > int32.high
+        of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high)
         else: false
 
       if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
 
     # Promote int literal to int64? Not always necessary, but more consistent
     if result.tokType == tkIntLit:
-      if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
+      if result.iNumber > high(int32):
         result.tokType = tkInt64Lit
 
   except ValueError:
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index b1ecf779e..165d75821 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -65,7 +65,7 @@ const
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
     warnXIsNeverRead: "'$1' is never read",
     warnXmightNotBeenInit: "'$1' might not have been initialized",
-    warnDeprecated: "$1 is deprecated",
+    warnDeprecated: "$1",
     warnConfigDeprecated: "config file '$1' is deprecated",
     warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
     warnUnknownMagic: "unknown magic '$1' might crash the compiler",
@@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
     warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
     hintSource, hintGlobalVar, hintGCStats}
   result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
-    hintProcessing, hintPattern, hintExecuting, hintLinking}
+    hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
 
 const
   NotesVerbosity* = computeNotesVerbosity()
diff --git a/compiler/lists.nim b/compiler/lists.nim
deleted file mode 100644
index bfd052204..000000000
--- a/compiler/lists.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module is deprecated, don't use it.
-# TODO Remove this
-
-import os
-
-static:
-  echo "WARNING: imported deprecated module compiler/lists.nim, use seq ore lists from the standard library"
-
-proc appendStr*(list: var seq[string]; data: string) {.deprecated.} =
-  # just use system.add
-  list.add(data)
-
-proc includeStr(list: var seq[string]; data: string): bool {.deprecated.} =
-  if list.contains(data):
-    result = true
-  else:
-    result = false
-    list.add data
-
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index db03ac2e0..8bc263485 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -89,7 +89,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
       prettybase.replaceDeprecated(conf, n.info, s, result)
     else:
       message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
-              s.name.s)
+              s.name.s & " is deprecated")
 
 proc localSearchInScope*(c: PContext, s: PIdent): PSym =
   result = strTableGet(c.currentScope.symbols, s)
@@ -150,7 +150,7 @@ type
 
 proc getSymRepr*(conf: ConfigRef; s: PSym): string =
   case s.kind
-  of skProc, skFunc, skMethod, skConverter, skIterator:
+  of routineKinds, skType:
     result = getProcHeader(conf, s)
   else:
     result = s.name.s
@@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
         # maybe they can be made skGenericParam as well.
         if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
            s.typ.kind != tyGenericParam:
-          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
+          message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
     s = nextIter(it, scope.symbols)
 
 proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
@@ -190,7 +190,7 @@ proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
     wrongRedefinition(c, info, sym.name.s, conflict.info)
 
 proc addDecl*(c: PContext, sym: PSym) =
-  let conflict = c.currentScope.addUniqueSym(sym)
+  let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
   if conflict != nil:
     wrongRedefinition(c, sym.info, sym.name.s, conflict.info)
 
diff --git a/compiler/main.nim b/compiler/main.nim
index 4e28ed483..49c2666ea 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -76,6 +76,8 @@ proc commandCompileToC(graph: ModuleGraph) =
   registerPass(graph, cgenPass)
 
   compileProject(graph)
+  if graph.config.errorCounter > 0:
+    return # issue #9933
   cgenWriteModules(graph.backend, conf)
   if conf.cmd != cmdRun:
     let proj = changeFileExt(conf.projectFull, "")
@@ -90,6 +92,7 @@ proc commandJsonScript(graph: ModuleGraph) =
 
 when not defined(leanCompiler):
   proc commandCompileToJS(graph: ModuleGraph) =
+    let conf = graph.config
     #incl(gGlobalOptions, optSafeCode)
     setTarget(graph.config.target, osJS, cpuJS)
     #initDefines()
@@ -98,6 +101,8 @@ when not defined(leanCompiler):
     semanticPasses(graph)
     registerPass(graph, JSgenPass)
     compileProject(graph)
+    if optGenScript in graph.config.globalOptions:
+      writeDepsFile(graph, toGeneratedFile(conf, conf.projectFull, ""))
 
 proc interactivePasses(graph: ModuleGraph) =
   initDefines(graph.config.symbols)
@@ -165,6 +170,7 @@ proc mainCommand*(graph: ModuleGraph) =
   of "c", "cc", "compile", "compiletoc":
     # compile means compileToC currently
     conf.cmd = cmdCompileToC
+    defineSymbol(graph.config.symbols, "c")
     commandCompileToC(graph)
   of "cpp", "compiletocpp":
     conf.cmd = cmdCompileToCpp
@@ -281,6 +287,7 @@ proc mainCommand*(graph: ModuleGraph) =
         (key: "defined_symbols", val: definedSymbols),
         (key: "lib_paths", val: %libpaths),
         (key: "out", val: %conf.outFile.string),
+        (key: "nimcache", val: %getNimcacheDir(conf).string),
         (key: "hints", val: hints),
         (key: "warnings", val: warnings),
       ]
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index f0718c4eb..9e27a2d7d 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
     # hacky way to implement 'x / y /../ z':
     result = renderTree(n, {renderNoComments}).replace(" ")
   of nkDotExpr:
-    localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths")
+    localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
     result = getModuleName(conf, n.sons[0])
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 7e6b67cbe..0dd5820b4 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -135,6 +135,10 @@ const
   WarningColor = fgYellow
   HintTitle    = "Hint: "
   HintColor    = fgGreen
+  # NOTE: currently line info line numbers start with 1,
+  # but column numbers start with 0, however most editors expect
+  # first column to be 1, so we need to +1 here
+  ColOffset*   = 1
 
 proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
 proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
@@ -155,7 +159,10 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
   if fileIdx.int32 < 0 or conf == nil:
     "???"
   else:
-    conf.m.fileInfos[fileIdx.int32].projPath.string
+    if optListFullPaths in conf.globalOptions:
+      conf.m.fileInfos[fileIdx.int32].fullPath.string
+    else:
+      conf.m.fileInfos[fileIdx.int32].projPath.string
 
 proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
   if fileIdx.int32 < 0 or conf == nil: result = "???"
@@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
     result = "???"
     return
   let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
-  let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
   if optListFullPaths in conf.globalOptions:
     result = absPath
   else:
+    let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
     result = if absPath.len < relPath.len: absPath else: relPath
 
 proc toLinenumber*(info: TLineInfo): int {.inline.} =
@@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
   result = toFilename(conf, info) & ":" & $info.line
 
 proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
-  result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
+  # consider calling `helpers.lineInfoToString` instead
+  result = toFilename(conf, info) & "(" & $info.line & ", " &
+    $(info.col + ColOffset) & ")"
 
 proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
 
@@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
         styledMsgWriteln(styleBright,
                          PosFormat % [toMsgFilename(conf, context.info),
                                       coordToStr(context.info.line.int),
-                                      coordToStr(context.info.col+1)],
+                                      coordToStr(context.info.col+ColOffset)],
                          resetStyle,
                          message)
     info = context.info
@@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
               of hintMin..hintMax: HintTitle
               else: ErrorTitle
   result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
-                        coordToStr(info.col+1)] &
+                        coordToStr(info.col+ColOffset)] &
            title &
            getMessageStr(msg, arg)
 
@@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
     color = HintColor
     if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
     inc(conf.hintCounter)
-  # NOTE: currently line info line numbers start with 1,
-  # but column numbers start with 0, however most editors expect
-  # first column to be 1, so we need to +1 here
   let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
-                       coordToStr(info.col+1)]
+                       coordToStr(info.col+ColOffset)]
   let s = getMessageStr(msg, arg)
 
   if not ignoreMsg:
diff --git a/compiler/options.nim b/compiler/options.nim
index 80d665d62..d39b0a268 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -40,7 +40,8 @@ type                          # please make sure we have under 32 options
     optMemTracker,
     optHotCodeReloading,
     optLaxStrings,
-    optNilSeqs
+    optNilSeqs,
+    optOldAst
 
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
@@ -77,7 +78,7 @@ type                          # please make sure we have under 32 options
     optShowAllMismatches      # show all overloading resolution candidates
     optWholeProject           # for 'doc2': output any dependency
     optMixedMode              # true if some module triggered C++ codegen
-    optListFullPaths
+    optListFullPaths          # use full paths in toMsgFilename, toFilename
     optNoNimblePath
     optDynlibOverrideAll
 
@@ -132,7 +133,7 @@ type
 
   TSystemCC* = enum
     ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
-    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
+    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
 
   CfileFlag* {.pure.} = enum
     Cached,    ## no need to recompile this time
@@ -482,16 +483,7 @@ proc setDefaultLibpath*(conf: ConfigRef) =
       conf.libpath = AbsoluteDir parentNimLibPath
 
 proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
-  # on Windows, 'expandFilename' calls getFullPathName which doesn't do
-  # case corrections, so we have to use this convoluted way of retrieving
-  # the true filename (see tests/modules and Nimble uses 'import Uri' instead
-  # of 'import uri'):
-  when defined(windows):
-    result = AbsoluteFile path.string.expandFilename
-    for x in walkFiles(result.string):
-      return AbsoluteFile x
-  else:
-    result = AbsoluteFile path.string.expandFilename
+  result = AbsoluteFile path.string.expandFilename
 
 proc shortenDir*(conf: ConfigRef; dir: string): string {.
     deprecated: "use 'relativeTo' instead".} =
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index bbaf7a069..db79e3eb9 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -115,20 +115,19 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
   else:
     patternError(p, conf)
 
-proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
+proc semNodeKindConstraints*(n: PNode; conf: ConfigRef; start: Natural): PNode =
   ## does semantic checking for a node kind pattern and compiles it into an
   ## efficient internal format.
-  assert p.kind == nkCurlyExpr
-  result = newNodeI(nkStrLit, p.info)
+  result = newNodeI(nkStrLit, n.info)
   result.strVal = newStringOfCap(10)
   result.strVal.add(chr(aqNone.ord))
-  if p.len >= 2:
-    for i in 1..<p.len:
-      compileConstraints(p.sons[i], result.strVal, conf)
+  if n.len >= 2:
+    for i in start..<n.len:
+      compileConstraints(n[i], result.strVal, conf)
     if result.strVal.len > MaxStackSize-1:
-      internalError(conf, p.info, "parameter pattern too complex")
+      internalError(conf, n.info, "parameter pattern too complex")
   else:
-    patternError(p, conf)
+    patternError(n, conf)
   result.strVal.add(ppEof)
 
 type
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 54b360a24..c9626c527 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
           parMessage(p, errIdentifierExpected, p.tok)
         break
       of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
+        let lineinfo = parLineinfo(p)
         var accm = ""
         while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
                                 tkParLe..tkParDotRi}:
           accm.add(tokToStr(p.tok))
           getTok(p)
-        result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p))
+        let node = newNodeI(nkIdent, lineinfo)
+        node.ident = p.lex.cache.getIdent(accm)
+        result.add(node)
       of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
         result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
         getTok(p)
@@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind,
   else:
     parMessage(p, errIdentifierExpected, p.tok)
 
-proc parseConstant(p: var TParser): PNode =
-  #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment
-  result = newNodeP(nkConstDef, p)
-  addSon(result, identWithPragma(p))
-  if p.tok.tokType == tkColon:
-    getTok(p)
-    optInd(p, result)
-    addSon(result, parseTypeDesc(p))
-  else:
-    addSon(result, p.emptyNode)
-  eat(p, tkEquals)
-  optInd(p, result)
-  addSon(result, parseExpr(p))
-  indAndComment(p, result)
-
 proc parseEnum(p: var TParser): PNode =
-  #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
+  #| enum = 'enum' optInd (symbol optPragmas optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
   getTok(p)
   addSon(result, p.emptyNode)
@@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode =
   while true:
     var a = parseSymbol(p)
     if a.kind == nkEmpty: return
+
+    var symPragma = a
+    var pragma: PNode
+    if p.tok.tokType == tkCurlyDotLe:
+      pragma = optPragmas(p)
+      symPragma = newNodeP(nkPragmaExpr, p)
+      addSon(symPragma, a)
+      addSon(symPragma, pragma)
+
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
-      add(result, a)
+      add(result, symPragma)
       break
+
     if p.tok.tokType == tkEquals and p.tok.indent < 0:
       getTok(p)
-      optInd(p, a)
-      var b = a
-      a = newNodeP(nkEnumFieldDef, p)
-      addSon(a, b)
-      addSon(a, parseExpr(p))
+      optInd(p, symPragma)
+      var b = symPragma
+      symPragma = newNodeP(nkEnumFieldDef, p)
+      addSon(symPragma, b)
+      addSon(symPragma, parseExpr(p))
       if p.tok.indent < 0 or p.tok.indent >= p.currInd:
-        rawSkipComment(p, a)
+        rawSkipComment(p, symPragma)
     if p.tok.tokType == tkComma and p.tok.indent < 0:
       getTok(p)
-      rawSkipComment(p, a)
+      rawSkipComment(p, symPragma)
     else:
       if p.tok.indent < 0 or p.tok.indent >= p.currInd:
-        rawSkipComment(p, a)
-    addSon(result, a)
+        rawSkipComment(p, symPragma)
+    addSon(result, symPragma)
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
         p.tok.tokType == tkEof:
       break
@@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode =
   result = newNodeP(nkObjectTy, p)
   getTok(p)
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
+    # Deprecated since v0.20.0
+    parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated")
     addSon(result, parsePragma(p))
   else:
     addSon(result, p.emptyNode)
@@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode =
 proc parseTypeDef(p: var TParser): PNode =
   #|
   #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
+  #|             indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
   #|             indAndComment?
   result = newNodeP(nkTypeDef, p)
-  addSon(result, identWithPragma(p, allowDot=true))
+  var identifier = identVis(p, allowDot=true)
+  var identPragma = identifier
+  var pragma: PNode
+  var genericParam: PNode
+  var noPragmaYet = true
+
+  if p.tok.tokType == tkCurlyDotLe:
+    pragma = optPragmas(p)
+    identPragma = newNodeP(nkPragmaExpr, p)
+    addSon(identPragma, identifier)
+    addSon(identPragma, pragma)
+    noPragmaYet = false
+
   if p.tok.tokType == tkBracketLe and p.validInd:
-    addSon(result, parseGenericParamList(p))
+    if not noPragmaYet:
+      # Deprecated since v0.20.0
+      parMessage(p, warnDeprecated, "pragma before generic parameter list is deprecated")
+    genericParam = parseGenericParamList(p)
   else:
-    addSon(result, p.emptyNode)
+    genericParam = p.emptyNode
+
+  if noPragmaYet:
+    pragma = optPragmas(p)
+    if pragma.kind != nkEmpty:
+      identPragma = newNodeP(nkPragmaExpr, p)
+      addSon(identPragma, identifier)
+      addSon(identPragma, pragma)
+  elif p.tok.tokType == tkCurlyDotLe:
+    parMessage(p, errGenerated, "pragma already present")
+
+  addSon(result, identPragma)
+  addSon(result, genericParam)
+
   if p.tok.tokType == tkEquals:
     result.info = parLineInfo(p)
     getTok(p)
@@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode =
   result[^1] = postExprBlocks(p, result[^1])
   indAndComment(p, result)
 
+proc parseConstant(p: var TParser): PNode =
+  #| constant = (parseVarTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
+  if p.tok.tokType == tkParLe: result = parseVarTuple(p)
+  else:
+    result = newNodeP(nkConstDef, p)
+    addSon(result, identWithPragma(p))
+    if p.tok.tokType == tkColon:
+      getTok(p)
+      optInd(p, result)
+      addSon(result, parseTypeDesc(p))
+    else:
+      addSon(result, p.emptyNode)
+    eat(p, tkEquals)
+    optInd(p, result)
+    addSon(result, parseExpr(p))
+    indAndComment(p, result)
+
 proc parseBind(p: var TParser, k: TNodeKind): PNode =
   #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
   #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 9ccd2240a..50fc13e1e 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -102,9 +102,9 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod
   for module in items(implicits):
     # implicit imports should not lead to a module importing itself
     if m.position != resolveMod(graph.config, module, relativeTo).int32:
-      var importStmt = newNodeI(nodeKind, gCmdLineInfo)
+      var importStmt = newNodeI(nodeKind, m.info)
       var str = newStrNode(nkStrLit, module)
-      str.info = gCmdLineInfo
+      str.info = m.info
       importStmt.addSon str
       if not processTopLevelStmt(graph, importStmt, a): break
 
@@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
     while true:
       openParsers(p, fileIdx, s, graph.cache, graph.config)
 
-      if sfSystemModule notin module.flags:
+      if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
         # XXX what about caching? no processing then? what if I change the
         # modules to include between compilation runs? we'd need to track that
         # in ROD files. I think we should enable this feature only
diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim
index 80c479898..7417845c0 100644
--- a/compiler/pathutils.nim
+++ b/compiler/pathutils.nim
@@ -17,11 +17,9 @@ type
   AbsoluteDir* = distinct string
   RelativeFile* = distinct string
   RelativeDir* = distinct string
+  AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
 
-proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0
-proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0
-proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0
-proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0
+proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0
 
 proc copyFile*(source, dest: AbsoluteFile) =
   os.copyFile(source.string, dest.string)
@@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
 
 proc createDir*(x: AbsoluteDir) {.borrow.}
 
+proc `$`*(x: AnyPath): string = x.string
+
 when true:
   proc eqImpl(x, y: string): bool {.inline.} =
     result = cmpPaths(x, y) == 0
 
-  proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string)
-  proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string)
-  proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string)
-  proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string)
+  proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)
 
   proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
     #assert isAbsolute(base.string)
@@ -88,6 +85,12 @@ when true:
 when isMainModule:
   doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
   doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim"
+  doAssert $RelativeDir"foo/bar" == "foo/bar"
+  doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar"
+  doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt"
+  doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt"
+  doAssert not RelativeDir"foo/bar".isEmpty
+  doAssert RelativeDir"".isEmpty
 
 when isMainModule and defined(windows):
   let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index f67e74f11..3967fa22d 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -73,6 +73,7 @@ const
                       wThread, wRaises, wLocks, wTags, wGcSafe}
   forVarPragmas* = {wInject, wGensym}
   allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
+  enumFieldPragmas* = {wDeprecated}
 
 proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
   let p = procAst[pragmasPos]
@@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
   # deprecated as of 0.18.1
   message(c.config, n.info, warnDeprecated,
           "use {.experimental: \"codeReordering.\".} instead; " &
-          (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}"))
+          (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
 
 proc processCallConv(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
@@ -446,14 +447,14 @@ proc processPop(c: PContext, n: PNode) =
 proc processDefine(c: PContext, n: PNode) =
   if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
     defineSymbol(c.config.symbols, n[1].ident.s)
-    message(c.config, n.info, warnDeprecated, "define")
+    message(c.config, n.info, warnDeprecated, "define is deprecated")
   else:
     invalidPragma(c, n)
 
 proc processUndef(c: PContext, n: PNode) =
   if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
     undefSymbol(c.config.symbols, n[1].ident.s)
-    message(c.config, n.info, warnDeprecated, "undef")
+    message(c.config, n.info, warnDeprecated, "undef is deprecated")
   else:
     invalidPragma(c, n)
 
@@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
       result.kind = n.kind # pragma(arg) -> pragma: arg
 
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
-                  validPragmas: TSpecialWords): bool =
+                  validPragmas: TSpecialWords, comesFromPush: bool) : bool =
   var it = n.sons[i]
   var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
   if key.kind == nkBracketExpr:
@@ -783,7 +784,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         if sym.kind in {skTemplate, skMacro}:
           incl(sym.flags, sfImmediate)
           incl(sym.flags, sfAllUntyped)
-          message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
+          message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate is deprecated")
         else: invalidPragma(c, it)
       of wDirty:
         if sym.kind == skTemplate: incl(sym.flags, sfDirty)
@@ -880,7 +881,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wExplain:
         sym.flags.incl sfExplain
       of wDeprecated:
-        if sym != nil and sym.kind in routineKinds + {skType}:
+        if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}:
           if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
           incl(sym.flags, sfDeprecated)
         elif sym != nil and sym.kind != skModule:
@@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
           c.selfName = considerQuotedIdent(c, it[1])
-          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
         elif it.kind == nkIdent or it.len == 1:
           c.selfName = getIdent(c.cache, "self")
-          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
         else:
           localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
@@ -1111,15 +1112,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         else: sym.flags.incl sfUsed
       of wLiftLocals: discard
       else: invalidPragma(c, it)
-    elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam, 
-                      skField, skProc, skFunc, skConverter, skMethod, skType}):
-      n.sons[i] = semCustomPragma(c, it)
-    elif sym != nil:
-      illegalCustomPragma(c, it, sym)
+    elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
+      discard "ignore the .push pragma; it doesn't apply"
     else:
-      invalidPragma(c, it)
+      if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
+                        skField, skProc, skFunc, skConverter, skMethod, skType}):
+        n.sons[i] = semCustomPragma(c, it)
+      elif sym != nil:
+        illegalCustomPragma(c, it, sym)
+      else:
+        invalidPragma(c, it)
+
+proc overwriteLineInfo(n: PNode; info: TLineInfo) =
+  n.info = info
+  for i in 0..<safeLen(n):
+    overwriteLineInfo(n[i], info)
 
 proc mergePragmas(n, pragmas: PNode) =
+  var pragmas = copyTree(pragmas)
+  overwriteLineInfo pragmas, n.info
   if n[pragmasPos].kind == nkEmpty:
     n[pragmasPos] = pragmas
   else:
@@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
         pushInfoContext(c.config, n.info)
         var i = 0
         while i < o.len:
-          if singlePragma(c, sym, o, i, validPragmas):
+          if singlePragma(c, sym, o, i, validPragmas, true):
             internalError(c.config, n.info, "implicitPragmas")
           inc i
         popInfoContext(c.config)
@@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
   if n == nil: return
   var i = 0
   while i < n.len:
-    if singlePragma(c, sym, n, i, validPragmas): break
+    if singlePragma(c, sym, n, i, validPragmas, false): break
     inc i
 
 proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index bfff86479..db60dafcc 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -42,15 +42,18 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
       proc (a: VmArgs) =
         body
 
-  template cbos(name, body) {.dirty.} =
+  template cbexc(name, exc, body) {.dirty.} =
     result.registerCallback "stdlib.system." & astToStr(name),
       proc (a: VmArgs) =
         errorMsg = ""
         try:
           body
-        except OSError:
+        except exc:
           errorMsg = getCurrentExceptionMsg()
 
+  template cbos(name, body) {.dirty.} =
+    cbexc(name, OSError, body)
+
   # Idea: Treat link to file as a file, but ignore link to directory to prevent
   # endless recursions out of the box.
   cbos listFiles:
@@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
     os.removeFile getString(a, 0)
   cbos createDir:
     os.createDir getString(a, 0)
-  cbos getOsError:
-    setResult(a, errorMsg)
+
+  result.registerCallback "stdlib.system.getError",
+    proc (a: VmArgs) = setResult(a, errorMsg)
+
   cbos setCurrentDir:
     os.setCurrentDir getString(a, 0)
   cbos getCurrentDir:
@@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
     setResult(a, os.getAppFilename())
   cbconf cppDefine:
     options.cppDefine(conf, a.getString(0))
+  cbexc stdinReadLine, EOFError:
+    setResult(a, "")
+    setResult(a, stdin.readLine())
+  cbexc stdinReadAll, EOFError:
+    setResult(a, "")
+    setResult(a, stdin.readAll())
 
 proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
                    freshDefines=true; conf: ConfigRef) =
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 8332af346..3763c9b84 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool =
   else: result = false
 
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
-  if n.kind == nkDefer:
-    localError(c.config, n.info, "defer statement not supported at top level")
   if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
     if sfSystemModule notin c.module.flags and not isEmptyTree(n):
       c.importTable.addSym c.graph.systemModule # import the "System" identifier
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index 5d676dc76..9f1ef313b 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -14,7 +14,7 @@
 
 type
   TLiftCtx = object
-    c: PContext
+    graph: ModuleGraph
     info: TLineInfo # for construction
     kind: TTypeAttachedOp
     fn: PSym
@@ -22,7 +22,7 @@ type
     recurse: bool
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
+proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym {.discardable.}
 
 proc at(a, i: PNode, elemType: PType): PNode =
@@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
 
 proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   for i in 0 ..< t.len:
-    let lit = lowerings.newIntLit(c.c.graph, x.info, i)
+    let lit = lowerings.newIntLit(c.graph, x.info, i)
     liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
 
 proc dotField(x: PNode, f: PSym): PNode =
@@ -49,6 +49,14 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
     liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
   of nkNilLit: discard
   of nkRecCase:
+    if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
+      ## the value needs to be destroyed before we assign the selector
+      ## or the value is lost
+      let prevKind = c.kind
+      c.kind = attachedDestructor
+      liftBodyObj(c, n, body, x, y)
+      c.kind = prevKind
+
     # copy the selector:
     liftBodyObj(c, n[0], body, x, y)
     # we need to generate a case statement:
@@ -66,26 +74,25 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
       liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
       caseStmt.add(branch)
     body.add(caseStmt)
-    localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
   of nkRecList:
     for t in items(n): liftBodyObj(c, t, body, x, y)
   else:
-    illFormedAstLocal(n, c.c.config)
+    illFormedAstLocal(n, c.graph.config)
 
-proc genAddr(c: PContext; x: PNode): PNode =
+proc genAddr(g: ModuleGraph; x: PNode): PNode =
   if x.kind == nkHiddenDeref:
-    checkSonsLen(x, 1, c.config)
+    checkSonsLen(x, 1, g.config)
     result = x.sons[0]
   else:
-    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
+    result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
     addSon(result, x)
 
-proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
+proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
   #if sfError in op.flags:
   #  localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
   result = newNodeI(nkCall, x.info)
   result.add newSymNode(op)
-  result.add genAddr(c, x)
+  result.add genAddr(g, x)
   result.add y
 
 proc newAsgnStmt(le, ri: PNode): PNode =
@@ -98,10 +105,10 @@ proc newOpCall(op: PSym; x: PNode): PNode =
   result.add(newSymNode(op))
   result.add x
 
-proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
+proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
   result = newNodeIT(nkCall, x.info, op.typ.sons[0])
   result.add(newSymNode(op))
-  result.add genAddr(c, x)
+  result.add genAddr(g, x)
 
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(op, y))
@@ -120,13 +127,13 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     else:
       op = field
       if op == nil:
-        op = liftBody(c.c, t, c.kind, c.info)
+        op = liftBody(c.graph, t, c.kind, c.info)
     if sfError in op.flags:
       incl c.fn.flags, sfError
     else:
-      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
+      markUsed(c.graph.config, c.info, op, c.graph.usageSym)
     onUse(c.info, op)
-    body.add newAsgnCall(c.c, op, x, y)
+    body.add newAsgnCall(c.graph, op, x, y)
     result = true
 
 proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
@@ -134,9 +141,9 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDestructor:
     let op = t.destructor
     if op != nil:
-      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
+      markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
-      body.add destructorCall(c.c, op, x)
+      body.add destructorCall(c.graph, op, x)
       result = true
   of attachedAsgn:
     result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
@@ -145,7 +152,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedDeepCopy:
     let op = t.deepCopy
     if op != nil:
-      markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
+      markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
       body.add newDeepCopyCall(op, x, y)
       result = true
@@ -162,13 +169,13 @@ proc addVar(father, v, value: PNode) =
   addSon(father, vpart)
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
-  temp.typ = getSysType(c.c.graph, body.info, tyInt)
+  var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
+  temp.typ = getSysType(c.graph, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
   var v = newNodeI(nkVarSection, c.info)
   result = newSymNode(temp)
-  v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
+  v.addVar(result, lowerings.newIntLit(c.graph, body.info, first))
   body.add v
 
 proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
@@ -178,22 +185,22 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
 
 proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
   result = newNodeI(nkWhileStmt, c.info, 2)
-  let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
-  cmp.add genHigh(c.c.graph, dest)
-  cmp.typ = getSysType(c.c.graph, c.info, tyBool)
+  let cmp = genBuiltin(c.graph, mLeI, "<=", i)
+  cmp.add genHigh(c.graph, dest)
+  cmp.typ = getSysType(c.graph, c.info, tyBool)
   result.sons[0] = cmp
   result.sons[1] = newNodeI(nkStmtList, c.info)
 
 proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
-  let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
-  incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
+  let incCall = genBuiltin(c.graph, mInc, "inc", i)
+  incCall.add lowerings.newIntLit(c.graph, c.info, 1)
   body.add incCall
 
-proc newSeqCall(c: PContext; x, y: PNode): PNode =
+proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
   # don't call genAddr(c, x) here:
-  result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
-  let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
-  lenCall.typ = getSysType(c.graph, x.info, tyInt)
+  result = genBuiltin(g, mNewSeq, "newSeq", x)
+  let lenCall = genBuiltin(g, mLengthSeq, "len", y)
+  lenCall.typ = getSysType(g, x.info, tyInt)
   result.add lenCall
 
 proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -204,7 +211,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     defaultOp(c, t, body, x, y)
   of tyArray:
     if tfHasAsgn in t.flags:
-      let i = declareCounter(c, body, firstOrd(c.c.config, t))
+      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
       let whileLoop = genWhileLoop(c, i, x)
       let elemType = t.lastSon
       liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -216,12 +223,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of tySequence:
     # note that tfHasAsgn is propagated so we need the check on
     # 'selectedGC' here to determine if we have the new runtime.
-    if c.c.config.selectedGC == gcDestructors:
+    if c.graph.config.selectedGC == gcDestructors:
       discard considerOverloadedOp(c, t, body, x, y)
     elif tfHasAsgn in t.flags:
       if c.kind != attachedDestructor:
-        body.add newSeqCall(c.c, x, y)
-      let i = declareCounter(c, body, firstOrd(c.c.config, t))
+        body.add newSeqCall(c.graph, x, y)
+      let i = declareCounter(c, body, firstOrd(c.graph.config, t))
       let whileLoop = genWhileLoop(c, i, x)
       let elemType = t.lastSon
       liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -235,11 +242,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       discard considerOverloadedOp(c, t, body, x, y)
     else:
       defaultOp(c, t, body, x, y)
-  of tyObject, tyDistinct:
+  of tyObject:
+    if not considerOverloadedOp(c, t, body, x, y):
+      liftBodyObj(c, t.n, body, x, y)
+  of tyDistinct:
     if not considerOverloadedOp(c, t, body, x, y):
-      if t.sons[0] != nil:
-        liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
-      if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
+      liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
   of tyTuple:
     liftBodyTup(c, t, body, x, y)
   of tyProc:
@@ -250,20 +258,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       # have to go through some indirection; we delegate this to the codegen:
       let call = newNodeI(nkCall, c.info, 2)
       call.typ = t
-      call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy))
+      call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
       call.sons[1] = y
       body.add newAsgnStmt(x, call)
   of tyVarargs, tyOpenArray:
-    localError(c.c.config, c.info, "cannot copy openArray")
+    localError(c.graph.config, c.info, "cannot copy openArray")
   of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
      tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
      tyTypeDesc, tyGenericInvocation, tyForward:
-    internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t))
+    internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
   of tyOrdinal, tyRange, tyInferred,
      tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
     liftBodyAux(c, lastSon(t), body, x, y)
-  of tyOptAsRef: internalError(c.c.config, "liftBodyAux")
+  of tyOptAsRef: internalError(c.graph.config, "liftBodyAux")
 
 proc newProcType(info: TLineInfo; owner: PSym): PType =
   result = newType(tyProc, owner)
@@ -279,26 +287,54 @@ proc addParam(procType: PType; param: PSym) =
   addSon(procType.n, newSymNode(param))
   rawAddSon(procType, param.typ)
 
-proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
+proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
+  assert typ.kind == tyDistinct
+  let baseType = typ[0]
+  case kind
+    of attachedAsgn:
+      if baseType.assignment == nil:
+        discard liftBody(g, baseType, kind, info)
+      typ.assignment = baseType.assignment
+      result = typ.assignment
+    of attachedSink:
+      if baseType.sink == nil:
+        discard liftBody(g, baseType, kind, info)
+      typ.sink = baseType.sink
+      result = typ.sink
+    of attachedDeepCopy:
+      if baseType.deepCopy == nil:
+        discard liftBody(g, baseType, kind, info)
+      typ.deepCopy = baseType.deepCopy
+      result = typ.deepCopy
+    of attachedDestructor:
+      if baseType.destructor == nil:
+        discard liftBody(g, baseType, kind, info)
+      typ.destructor = baseType.destructor
+      result = typ.destructor
+
+proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
+  if typ.kind == tyDistinct:
+    return liftBodyDistinctType(g, typ, kind, info)
+
   var a: TLiftCtx
   a.info = info
-  a.c = c
+  a.graph = g
   a.kind = kind
   let body = newNodeI(nkStmtList, info)
   let procname = case kind
-                 of attachedAsgn: getIdent(c.cache, "=")
-                 of attachedSink: getIdent(c.cache, "=sink")
-                 of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
-                 of attachedDestructor: getIdent(c.cache, "=destroy")
+                 of attachedAsgn: getIdent(g.cache, "=")
+                 of attachedSink: getIdent(g.cache, "=sink")
+                 of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
+                 of attachedDestructor: getIdent(g.cache, "=destroy")
 
   result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
   a.asgnForType = typ
 
-  let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
-  let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
-  dest.typ = makeVarType(c, typ)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
+  let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
+  dest.typ = makeVarType(typ.owner, typ)
   src.typ = typ
 
   result.typ = newProcType(info, typ.owner)
@@ -309,7 +345,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
   # recursion is handled explicitly, do not register the type based operation
   # before 'liftBodyAux':
-  if c.config.selectedGC == gcDestructors and
+  if g.config.selectedGC == gcDestructors and
       typ.kind in {tySequence, tyString} and body.len == 0:
     discard "do not cache it yet"
   else:
@@ -328,17 +364,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   incl result.flags, sfFromGeneric
 
 
-proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
+proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym =
   let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
   result = t.assignment
   if result.isNil:
-    result = liftBody(c, t, attachedAsgn, info)
+    result = liftBody(g, t, attachedAsgn, info)
 
-proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
-  let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
-  result = newAsgnCall(c, a, dest, src)
+proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
+  let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
+  result = newAsgnCall(g, a, dest, src)
 
-proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
+proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'destroyer' pass depends on it.
@@ -350,11 +386,11 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
   let typ = typ.skipTypes({tyGenericInst, tyAlias})
   # we generate the destructor first so that other operators can depend on it:
   if typ.destructor == nil:
-    liftBody(c, typ, attachedDestructor, info)
+    liftBody(g, typ, attachedDestructor, info)
   if typ.assignment == nil:
-    liftBody(c, typ, attachedAsgn, info)
+    liftBody(g, typ, attachedAsgn, info)
   if typ.sink == nil:
-    liftBody(c, typ, attachedSink, info)
+    liftBody(g, typ, attachedSink, info)
 
-#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode =
+#proc patchResolvedTypeBoundOp*(g: ModuleGraph; n: PNode): PNode =
 #  if n.kind == nkCall and
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 7e0ea5490..3723d3fc1 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -287,7 +287,18 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
 
   let ident = considerQuotedIdent(c, f, n).s
   if nfDotField in n.flags and nfExplicitCall notin n.flags:
-    result = errUndeclaredField % ident & result
+    let sym = n.sons[1].typ.sym
+    var typeHint = ""
+    if sym == nil:
+      # Perhaps we're in a `compiles(foo.bar)` expression, or
+      # in a concept, eg:
+      #   ExplainedConcept {.explain.} = concept x
+      #     x.foo is int
+      # We coudl use: `(c.config $ n.sons[1].info)` to get more context.
+      discard
+    else:
+      typeHint = " for type " & getProcHeader(c.config, sym)
+    result = errUndeclaredField % ident & typeHint & " " & result
   else:
     if result.len == 0: result = errUndeclaredRoutine % ident
     else: result = errBadRoutine % [ident, result]
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 735c6f6b1..a05bda32d 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
     result = newTypeS(kind, c)
     addSonSkipIntLit(result, baseType)
 
+proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType =
+  if baseType.kind == kind:
+    result = baseType
+  else:
+    result = newType(kind, owner)
+    addSonSkipIntLit(result, baseType)
+
 proc makeTypeDesc*(c: PContext, typ: PType): PType =
   if typ.kind == tyTypeDesc:
     result = typ
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index ddec457a1..82f948492 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -108,6 +108,8 @@ const
 
 proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
   result = convOK
+  # We're interested in the inner type and not in the static tag
+  var src = src.skipTypes({tyStatic})
   if sameType(castDest, src) and castDest.sym == src.sym:
     # don't annoy conversions that may be needed on another processor:
     if castDest.kind notin IntegralTypes+{tyRange}:
@@ -230,7 +232,7 @@ proc semConv(c: PContext, n: PNode): PNode =
   # special case to make MyObject(x = 3) produce a nicer error message:
   if n[1].kind == nkExprEqExpr and
       targetType.skipTypes(abstractPtrs).kind == tyObject:
-    localError(c.config, n.info, "object contruction uses ':', not '='")
+    localError(c.config, n.info, "object construction uses ':', not '='")
   var op = semExprWithType(c, n.sons[1])
   if targetType.isMetaType:
     let final = inferWithMetatype(c, targetType, op, true)
@@ -403,8 +405,8 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n[1] = makeTypeSymNode(c, lhsType, n[1].info)
       lhsType = n[1].typ
   else:
-    internalAssert c.config, lhsType.base.kind != tyNone
-    if c.inGenericContext > 0 and lhsType.base.containsGenericType:
+    if lhsType.base.kind == tyNone or
+        (c.inGenericContext > 0 and lhsType.base.containsGenericType):
       # BUGFIX: don't evaluate this too early: ``T is void``
       return
 
@@ -710,16 +712,20 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
       let a = getConstExpr(c.module, n.sons[i], c.graph)
       if a == nil: return n
       call.add(a)
+
     #echo "NOW evaluating at compile time: ", call.renderTree
-    if sfCompileTime in callee.flags:
-      result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
-      if result.isNil:
-        localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
-      else: result = fixupTypeAfterEval(c, result, n)
+    if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
+      if sfCompileTime in callee.flags:
+        result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
+        if result.isNil:
+          localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
+        else: result = fixupTypeAfterEval(c, result, n)
+      else:
+        result = evalConstExpr(c.module, c.graph, call)
+        if result.isNil: result = n
+        else: result = fixupTypeAfterEval(c, result, n)
     else:
-      result = evalConstExpr(c.module, c.graph, call)
-      if result.isNil: result = n
-      else: result = fixupTypeAfterEval(c, result, n)
+      result = n
     #if result != n:
     #  echo "SUCCESS evaluated at compile time: ", call.renderTree
 
@@ -798,7 +804,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
       result = magicsAfterOverloadResolution(c, result, flags)
     if result.typ != nil and
         not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
-      liftTypeBoundOps(c, result.typ, n.info)
+      liftTypeBoundOps(c.graph, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
   if c.matchedConcept == nil:
     result = evalAtCompileTime(c, result)
@@ -913,7 +919,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
   let isPush = hintExtendedContext in c.config.notes
   if isPush: pushInfoContext(c.config, n.info)
   result = semExpr(c, n, {efWantStmt})
-  discardCheck(c, result, {})
+  result = discardCheck(c, result, {})
   if isPush: popInfoContext(c.config)
 
 proc isTypeExpr(n: PNode): bool =
@@ -1039,7 +1045,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
   of skConst:
     markUsed(c.config, n.info, s, c.graph.usageSym)
     onUse(n.info, s)
-    case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
+    let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
+    case typ.kind
     of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
         tyTuple, tySet, tyUInt..tyUInt64:
       if s.magic == mNone: result = inlineConst(c, n, s)
@@ -1057,6 +1064,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
       # deal with two different ``[]``.
       if s.ast.len == 0: result = inlineConst(c, n, s)
       else: result = newSymNode(s, n.info)
+    of tyStatic:
+      if typ.n != nil:
+        result = typ.n
+        result.typ = typ.base
+      else:
+        result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
   of skMacro:
@@ -1581,7 +1594,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
 
     n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
-    liftTypeBoundOps(c, lhs.typ, lhs.info)
+    liftTypeBoundOps(c.graph, lhs.typ, lhs.info)
     #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
 
     fixAbstractType(c, n)
@@ -1628,7 +1641,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       a.sons[1] = result
       result = semAsgn(c, a)
   else:
-    discardCheck(c, result, {})
+    result = discardCheck(c, result, {})
 
   if c.p.owner.kind notin {skMacro, skTemplate} and
      c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
@@ -2324,7 +2337,6 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
 
 proc semExport(c: PContext, n: PNode): PNode =
   result = newNodeI(nkExportStmt, n.info)
-
   for i in 0..<n.len:
     let a = n.sons[i]
     var o: TOverloadIter
@@ -2343,6 +2355,9 @@ proc semExport(c: PContext, n: PNode): PNode =
         it = nextIter(ti, s.tab)
     else:
       while s != nil:
+        if s.kind == skEnumField:
+          localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
+            "; enum field cannot be exported individually")
         if s.kind in ExportableSymKinds+{skModule}:
           result.add(newSymNode(s, a.info))
           strTableAdd(c.module.tab, s)
@@ -2430,7 +2445,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
       result.kind = nkCall
       result = semExpr(c, result, flags)
   of nkBind:
-    message(c.config, n.info, warnDeprecated, "bind")
+    message(c.config, n.info, warnDeprecated, "bind is deprecated")
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
     if c.matchedConcept != nil and n.len == 1:
@@ -2620,6 +2635,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkStaticStmt:
     result = semStaticStmt(c, n)
   of nkDefer:
+    if c.currentScope == c.topLevelScope:
+      localError(c.config, n.info, "defer statement not supported at top level")
     n.sons[0] = semExpr(c, n.sons[0])
     if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
       localError(c.config, n.info, "'defer' takes a 'void' expression")
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 07321f477..d65d962cb 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -19,6 +19,9 @@ type
     c: PContext
 
 proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
+  if c.field != nil and isEmptyType(c.field.typ):
+    result = newNode(nkEmpty)
+    return
   case n.kind
   of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
   of nkIdent, nkSym:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 9e7ed5cee..0bdd0b64c 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
           return field.name.s
         else:
           return field.ast.strVal
-    internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
+    localError(g.config, a.info,
+      "Cannot convert int literal to $1. The value is invalid." %
+        [typeToString(t)])
   else:
     result = $x
 
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 7e61854b8..05c8b181c 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
   template typeWithSonsResult(kind, sons): PNode =
     newTypeWithSons(context, kind, sons).toNode(traitCall.info)
 
-  case trait.sym.name.s
+  let s = trait.sym.name.s
+  case s
   of "or", "|":
     return typeWithSonsResult(tyOr, @[operand, operand2])
   of "and":
     return typeWithSonsResult(tyAnd, @[operand, operand2])
   of "not":
     return typeWithSonsResult(tyNot, @[operand])
-  of "name":
+  of "name", "$":
     result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
     result.typ = newType(tyString, context)
     result.info = traitCall.info
@@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
                      hasDestructor(t)
     result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
   else:
-    localError(c.config, traitCall.info, "unknown trait")
+    localError(c.config, traitCall.info, "unknown trait: " & s)
     result = newNodeI(nkEmpty, traitCall.info)
 
 proc semTypeTraits(c: PContext, n: PNode): PNode =
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 5be57f614..288820d86 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -95,13 +95,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
   elif efInTypeof in flags:
     result.typ = n[1].typ
 
-proc toCover(c: PContext, t: PType): BiggestInt =
-  let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
-  if t2.kind == tyEnum and enumHasHoles(t2):
-    result = sonsLen(t2.n)
-  else:
-    result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
-
 proc semProc(c: PContext, n: PNode): PNode
 
 proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
@@ -137,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) =
     for it in n: fixNilType(c, it)
   n.typ = nil
 
-proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
+proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
+  result = expr
   if c.matchedConcept != nil or efInTypeof in flags: return
 
   if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
     if implicitlyDiscardable(result):
-      var n = newNodeI(nkDiscardStmt, result.info, 1)
-      n[0] = result
+      result = newNode(nkDiscardStmt, result.info, @[result])
     elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
       var n = result
       while n.kind in skipForDiscardable: n = n.lastSon
@@ -175,7 +168,8 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
     else: illFormedAst(it, c.config)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
       (not hasElse and efInTypeof notin flags):
-    for it in n: discardCheck(c, it.lastSon, flags)
+    for it in n: 
+      it.sons[^1] = discardCheck(c, it.sons[^1], flags)
     result.kind = nkIfStmt
     # propagate any enforced VoidContext:
     if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
@@ -273,12 +267,14 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
 
   dec c.p.inTryStmt
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
-    discardCheck(c, n.sons[0], flags)
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
+    n.sons[0] = discardCheck(c, n.sons[0], flags)
+    for i in 1..n.len-1:
+      n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
     if typ == c.enforceVoidContext:
       result.typ = c.enforceVoidContext
   else:
-    if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
+    if n.lastSon.kind == nkFinally:
+      n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
     n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
     for i in 1..last:
       var it = n.sons[i]
@@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     result = semIdentWithPragma(c, kind, n, {})
     if result.owner.kind == skModule:
       incl(result.flags, sfGlobal)
-  suggestSym(c.config, n.info, result, c.graph.usageSym)
+
+  proc getLineInfo(n: PNode): TLineInfo =
+    case n.kind
+    of nkPostfix:
+      getLineInfo(n.sons[1])
+    of nkAccQuoted, nkPragmaExpr:
+      getLineInfo(n.sons[0])
+    else:
+      n.info
+  let info = getLineInfo(n)
+  suggestSym(c.config, info, result, c.graph.usageSym)
 
 proc checkNilable(c: PContext; v: PSym) =
   if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
       {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
-    if v.ast.isNil:
+    if v.astdef.isNil:
       message(c.config, v.info, warnProveInit, v.name.s)
-    elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
+    elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
       message(c.config, v.info, warnProveInit, v.name.s)
 
 include semasgn
@@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
     # this can only happen for errornous var statements:
     if typ == nil: continue
     typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
-    liftTypeBoundOps(c, typ, a.info)
+    liftTypeBoundOps(c.graph, typ, a.info)
     var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
     if a.kind == nkVarTuple:
       if tup.kind != tyTuple:
@@ -515,8 +521,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
               message(c.config, a.info, warnShadowIdent, v.name.s)
       if a.kind != nkVarTuple:
         if def.kind != nkEmpty:
-          # this is needed for the evaluation pass and for the guard checking:
-          v.ast = def
           if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
         setVarType(c, v, typ)
         b = newNodeI(nkIdentDefs, a.info)
@@ -528,6 +532,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         addSon(b, a.sons[length-2])
         addSon(b, copyTree(def))
         addToVarSection(c, result, n, b)
+        if optOldAst in c.config.options:
+          if def.kind != nkEmpty:
+            v.ast = def
+        else:
+          # this is needed for the evaluation pass, guard checking
+          #  and custom pragmas:
+          var ast = newNodeI(nkIdentDefs, a.info)
+          if a[j].kind == nkPragmaExpr:
+            var p = newNodeI(nkPragmaExpr, a.info)
+            p.add newSymNode(v)
+            p.add a[j][1].copyTree
+            ast.add p
+          else:
+            ast.add newSymNode(v)
+          ast.add a.sons[length-2].copyTree
+          ast.add def
+          v.ast = ast
       else:
         if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
         # bug #7663, for 'nim check' this can be a non-tuple:
@@ -545,21 +566,22 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
+  inc c.inStaticContext
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
     if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
     if a.kind == nkCommentStmt: continue
-    if a.kind != nkConstDef: illFormedAst(a, c.config)
-    checkSonsLen(a, 3, c.config)
-    var v = semIdentDef(c, a.sons[0], skConst)
-    styleCheckDef(c.config, v)
-    onDef(a[0].info, v)
+    if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
+    checkMinSonsLen(a, 3, c.config)
+    var length = sonsLen(a)
+
     var typ: PType = nil
-    if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
+    if a.sons[length-2].kind != nkEmpty:
+      typ = semTypeNode(c, a.sons[length-2], nil)
 
-    var def = semConstExpr(c, a.sons[2])
+    var def = semConstExpr(c, a.sons[length-1])
     if def == nil:
-      localError(c.config, a.sons[2].info, errConstExprExpected)
+      localError(c.config, a.sons[length-1].info, errConstExprExpected)
       continue
     # check type compatibility between def.typ and typ:
     if typ != nil:
@@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode =
     else:
       typ = def.typ
     if typ == nil:
-      localError(c.config, a.sons[2].info, errConstExprExpected)
+      localError(c.config, a.sons[length-1].info, errConstExprExpected)
       continue
     if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
       localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
       continue
-    setVarType(c, v, typ)
-    v.ast = def               # no need to copy
-    if sfGenSym notin v.flags: addInterfaceDecl(c, v)
-    elif v.owner == nil: v.owner = getCurrOwner(c)
-    var b = newNodeI(nkConstDef, a.info)
-    if importantComments(c.config): b.comment = a.comment
-    addSon(b, newSymNode(v))
-    addSon(b, a.sons[1])
-    addSon(b, copyTree(def))
-    addSon(result, b)
+
+    var b: PNode
+    if a.kind == nkVarTuple:
+      if typ.kind != tyTuple:
+        localError(c.config, a.info, errXExpected, "tuple")
+      elif length-2 != sonsLen(typ):
+        localError(c.config, a.info, errWrongNumberOfVariables)
+      b = newNodeI(nkVarTuple, a.info)
+      newSons(b, length)
+      b.sons[length-2] = a.sons[length-2]
+      b.sons[length-1] = def
+
+    for j in countup(0, length-3):
+      var v = semIdentDef(c, a.sons[j], skConst)
+      if sfGenSym notin v.flags: addInterfaceDecl(c, v)
+      elif v.owner == nil: v.owner = getCurrOwner(c)
+      styleCheckDef(c.config, v)
+      onDef(a[j].info, v)
+
+      if a.kind != nkVarTuple:
+        setVarType(c, v, typ)
+        v.ast = def               # no need to copy
+        b = newNodeI(nkConstDef, a.info)
+        if importantComments(c.config): b.comment = a.comment
+        addSon(b, newSymNode(v))
+        addSon(b, a.sons[1])
+        addSon(b, copyTree(def))
+      else:
+        setVarType(c, v, typ.sons[j])
+        v.ast = def[j]
+        b.sons[j] = newSymNode(v)
+    addSon(result,b)
+  dec c.inStaticContext
 
 include semfields
 
@@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
   openScope(c)
   n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
   if efInTypeof notin flags:
-    discardCheck(c, n.sons[length-1], flags)
+    n.sons[^1] = discardCheck(c, n.sons[^1], flags)
   closeScope(c)
   dec(c.p.nestedLoopCounter)
 
@@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
   closeScope(c)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
       (not hasElse and efInTypeof notin flags):
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
+    for i in 1..n.len-1:
+      n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
     # propagate any enforced VoidContext:
     if typ == c.enforceVoidContext:
       result.typ = c.enforceVoidContext
@@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         #debug s.typ
       s.ast = a
       popOwner(c)
+    if sfExportc in s.flags and s.typ.kind == tyAlias:
+      localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
     let aa = a.sons[2]
     if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
        aa.sons[0].kind == nkObjectTy:
@@ -1090,9 +1138,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
 
 
 proc checkForMetaFields(c: PContext; n: PNode) =
-  template checkMeta(t) =
+  proc checkMeta(c: PContext; n: PNode; t: PType) =
     if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
-      localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
+      if t.kind == tyBuiltInTypeClass and t.len == 1 and t.sons[0].kind == tyProc:
+        localError(c.config, n.info, ("'$1' is not a concrete type; " &
+          "for a callback without parameters use 'proc()'") % t.typeToString)
+      else:
+        localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
 
   if n.isNil: return
   case n.kind
@@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
        tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
       let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
       for i in start ..< t.len:
-        checkMeta(t.sons[i])
+        checkMeta(c, n, t.sons[i])
     else:
-      checkMeta(t)
+      checkMeta(c, n, t)
   else:
     internalAssert c.config, false
 
@@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
 
 proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
   s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
-  if s.kind notin {skMacro, skTemplate}:
-    if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
-      localError(c.config, n.info, "invalid return type: 'stmt'")
 
 proc addParams(c: PContext, n: PNode, kind: TSymKind) =
   for i in countup(1, sonsLen(n)-1):
@@ -1423,8 +1472,15 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
     addResult(c, s.typ.sons[0], n.info, s.kind)
     addResultNode(c, n)
 
+proc canonType(c: PContext, t: PType): PType =
+  if t.kind == tySequence:
+    result = c.graph.sysTypes[tySequence]
+  else:
+    result = t
+
 proc semOverride(c: PContext, s: PSym, n: PNode) =
-  case s.name.s.normalize
+  let name = s.name.s.normalize
+  case name
   of "=destroy":
     let t = s.typ
     var noError = false
@@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
         else: break
       if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
+        obj = canonType(c, obj)
         if obj.destructor.isNil:
           obj.destructor = s
         else:
           localError(c.config, n.info, errGenerated,
             "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
         noError = true
+        if obj.owner.getModule != s.getModule:
+          localError(c.config, n.info, errGenerated,
+            "type bound operation `=destroy` can be defined only in the same module with its type (" & obj.typeToString() & ")")
     if not noError and sfSystemModule notin s.owner.flags:
       localError(c.config, n.info, errGenerated,
         "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
@@ -1465,6 +1525,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       else:
         localError(c.config, n.info, errGenerated,
                    "cannot bind 'deepCopy' to: " & typeToString(t))
+
+      if t.owner.getModule != s.getModule:
+          localError(c.config, n.info, errGenerated,
+            "type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")")
+
     else:
       localError(c.config, n.info, errGenerated,
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
@@ -1487,12 +1552,18 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
           objB = objB.sons[0]
         else: break
       if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
+        # attach these ops to the canonical tySequence
+        obj = canonType(c, obj)
         let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
         if opr[].isNil:
           opr[] = s
         else:
           localError(c.config, n.info, errGenerated,
                      "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+        if obj.owner.getModule != s.getModule:
+            localError(c.config, n.info, errGenerated,
+              "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
+
         return
     if sfSystemModule notin s.owner.flags:
       localError(c.config, n.info, errGenerated,
@@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
           foundObj = true
           x.methods.add((col,s))
     if not foundObj:
-      message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
+      message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated")
   else:
     # why check for the body? bug #2400 has none. Checking for sfForward makes
     # no sense either.
@@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
     elif i != last or voidContext:
-      discardCheck(c, n.sons[i], flags)
+      n.sons[i] = discardCheck(c, n.sons[i], flags)
     else:
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 7159e17e7..c28902b1f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -80,9 +80,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if isPure: initStrTable(symbols)
   var hasNull = false
   for i in countup(1, sonsLen(n) - 1):
+    if n.sons[i].kind == nkEmpty: continue
     case n.sons[i].kind
     of nkEnumFieldDef:
-      e = newSymS(skEnumField, n.sons[i].sons[0], c)
+      if n.sons[i].sons[0].kind == nkPragmaExpr:
+        e = newSymS(skEnumField, n.sons[i].sons[0].sons[0], c)
+        pragma(c, e, n.sons[i].sons[0].sons[1], enumFieldPragmas)
+      else:
+        e = newSymS(skEnumField, n.sons[i].sons[0], c)
       var v = semConstExpr(c, n.sons[i].sons[1])
       var strVal: PNode = nil
       case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
@@ -91,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
           strVal = v.sons[1] # second tuple part is the string value
           if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
             if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true):
-              localError(c.config, v.sons[0].info, errOrdinalTypeExpected)
+              localError(c.config, v.sons[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v.sons[0].typ, preferDesc))
             x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
           else:
             localError(c.config, strVal.info, errStringLiteralExpected)
@@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
         x = counter
       else:
         if not isOrdinalType(v.typ, allowEnumWithHoles=true):
-          localError(c.config, v.info, errOrdinalTypeExpected)
+          localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc))
         x = getOrdValue(v)
       if i != 1:
         if x != counter: incl(result.flags, tfEnumHasHoles)
@@ -115,6 +120,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       e = n.sons[i].sym
     of nkIdent, nkAccQuoted:
       e = newSymS(skEnumField, n.sons[i], c)
+    of nkPragmaExpr:
+      e = newSymS(skEnumField, n.sons[i].sons[0], c)
+      pragma(c, e, n.sons[i].sons[1], enumFieldPragmas)
     else:
       illFormedAst(n[i], c.config)
     e.typ = result
@@ -133,7 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
       wrongRedefinition(c, e.info, e.name.s, conflict.info)
     inc(counter)
-  if not hasNull: incl(result.flags, tfNeedsInit)
+  if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfNeedsInit)
 
 proc semSet(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tySet, prev, c)
@@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
               tyError, tyObject}:
           message c.config, n[i].info, errGenerated, "region needs to be an object type"
         else:
-          message(c.config, n.info, warnDeprecated, "region for pointer types")
+          message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
         addSonSkipIntLit(result, region)
     addSonSkipIntLit(result, t)
     if tfPartial in result.flags:
@@ -591,6 +599,13 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
   for i in lastIndex.succ..(sonsLen(branch) - 2):
     checkForOverlap(c, t, i, branchIndex)
 
+proc toCover(c: PContext, t: PType): BiggestInt =
+  let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
+  if t2.kind == tyEnum and enumHasHoles(t2):
+    result = sonsLen(t2.n)
+  else:
+    result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
+
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
                       father: PNode, rectype: PType, hasCaseFields = false)
 proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
@@ -603,15 +618,16 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
     return
   incl(a.sons[0].sym.flags, sfDiscriminant)
   var covered: BiggestInt = 0
+  var chckCovered = false
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
-  if not isOrdinalType(typ):
-    localError(c.config, n.info, "selector must be of an ordinal type")
-  elif firstOrd(c.config, typ) != 0:
-    localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
-                                     ") must be 0 for discriminant")
-  elif lengthOrd(c.config, typ) > 0x00007FFF:
-    localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
-  var chckCovered = true
+  case typ.kind
+  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool, tyRange:
+    chckCovered = true
+  of tyFloat..tyFloat128, tyString, tyError:
+    discard
+  else:
+    if not isOrdinalType(typ):
+      localError(c.config, n.info, "selector must be of an ordinal type, float or string")
   for i in countup(1, sonsLen(n) - 1):
     var b = copyTree(n.sons[i])
     addSon(a, b)
@@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
       checkMinSonsLen(b, 2, c.config)
       semCaseBranch(c, a, b, i, covered)
     of nkElse:
-      chckCovered = false
       checkSonsLen(b, 1, c.config)
+      if chckCovered and covered == toCover(c, a.sons[0].typ):
+        localError(c.config, b.info, "invalid else, all cases are already covered")
+      chckCovered = false
     else: illFormedAst(n, c.config)
     delSon(b, sonsLen(b) - 1)
     semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true)
-  if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
+  if chckCovered and covered != toCover(c, a.sons[0].typ):
     localError(c.config, a.info, "not all cases are covered")
   addSon(father, a)
 
@@ -1010,7 +1028,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
 proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
   if n.kind == nkCurlyExpr:
     result = semTypeNode(c, n.sons[0], nil)
-    constraint = semNodeKindConstraints(n, c.config)
+    constraint = semNodeKindConstraints(n, c.config, 1)
+  elif n.kind == nkCall and
+      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
+      considerQuotedIdent(c, n[0]).s == "{}":
+    result = semTypeNode(c, n[1], nil)
+    constraint = semNodeKindConstraints(n, c.config, 2)
   else:
     result = semTypeNode(c, n, nil)
 
@@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
 
     if hasType:
       typ = semParamType(c, a.sons[length-2], constraint)
+      var owner = getCurrOwner(c).owner
+      # TODO: Disallow typed/untyped in procs in the compiler/stdlib
+      if (owner.kind != skModule or owner.owner.name.s != "stdlib") and
+        kind == skProc and (typ.kind == tyStmt or typ.kind == tyExpr):
+          localError(c.config, a.sons[length-2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros")
 
     if hasDefault:
       def = a[^1]
@@ -1130,12 +1158,15 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     # turn explicit 'void' return type into 'nil' because the rest of the
     # compiler only checks for 'nil':
     if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
+      if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}:
+        localError(c.config, n.sons[0].info, "return type '" & typeToString(r) & 
+            "' is only valid for macros and templates")
       # 'auto' as a return type does not imply a generic:
-      if r.kind == tyAnything:
+      elif r.kind == tyAnything:
         # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
         # compiler is hardly aware of 'auto':
         r = newTypeS(tyExpr, c)
-      elif r.kind != tyExpr:
+      else:
         if r.sym == nil or sfAnon notin r.sym.flags:
           let lifted = liftParamType(c, kind, genericParams, r, "result",
                                      n.sons[0].info)
@@ -1287,7 +1318,19 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
     semObjectTypeForInheritedGenericInst(c, n, tx)
 
-proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType
+proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
+  if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil:
+    result = newTypeS(tyAlias, c)
+    result.rawAddSon typeExpr
+    result.sym = prev.sym
+    assignType(prev, result)
+
+proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
+  if prev != nil:
+    let result = newTypeS(tyAlias, c)
+    result.rawAddSon typExpr.typ
+    result.sym = prev.sym
+    assignType(prev, result)
 
 proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
   var n = semExprWithType(c, n, {efDetermineType})
@@ -1391,20 +1434,6 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
     when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
   closeScope(c)
 
-proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
-  if typeExpr.kind in {tyObject, tyEnum, tyDistinct} and prev != nil:
-    result = newTypeS(tyAlias, c)
-    result.rawAddSon typeExpr
-    result.sym = prev.sym
-    assignType(prev, result)
-
-proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
-  if prev != nil:
-    let result = newTypeS(tyAlias, c)
-    result.rawAddSon typExpr.typ
-    result.sym = prev.sym
-    assignType(prev, result)
-
 proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
   if n.kind == nkType:
     result = symFromType(c, n.typ, n.info)
@@ -1551,10 +1580,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         # XXX figure out why this has children already...
         result.sons.setLen 0
         result.n = nil
-        if c.config.selectedGc == gcDestructors:
-          result.flags = {tfHasAsgn}
-        else:
-          result.flags = {}
+        result.flags = {tfHasAsgn}
         semContainerArg(c, n, "seq", result)
       else:
         result = semContainer(c, n, tySequence, "seq", prev)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index d66e8d121..e08559db6 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -2071,6 +2071,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     # constructor in a call:
     if result == nil and f.kind == tyVarargs:
       if f.n != nil:
+        # Forward to the varargs converter
         result = localConvMatch(c, m, f, a, arg)
       else:
         r = typeRel(m, base(f), a)
@@ -2083,10 +2084,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
         # bug #4799, varargs accepting subtype relation object
         elif r == isSubtype:
           inc(m.subtypeMatches)
-          if f.kind == tyTypeDesc:
+          if base(f).kind == tyTypeDesc:
             result = arg
           else:
-            result = implicitConv(nkHiddenSubConv, f, arg, m, c)
+            result = implicitConv(nkHiddenSubConv, base(f), arg, m, c)
           m.baseTypeMatch = true
         else:
           result = userConvMatch(c, m, base(f), a, arg)
@@ -2231,14 +2232,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
       else:
         m.state = csNoMatch
         return
-    
+
     if formal.typ.kind == tyVar:
-      let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg
-      if arg_converter.kind == nkHiddenCallConv:
-        if arg_converter.typ.kind != tyVar:
+      let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
+      if argConverter.kind == nkHiddenCallConv:
+        if argConverter.typ.kind != tyVar:
           m.state = csNoMatch
           m.mutabilityProblem = uint8(f-1)
-          return  
+          return
       elif not n.isLValue:
         m.state = csNoMatch
         m.mutabilityProblem = uint8(f-1)
@@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
 
       if n.sons[a].kind == nkHiddenStdConv:
         doAssert n.sons[a].sons[0].kind == nkEmpty and
-                 n.sons[a].sons[1].kind == nkArgList and
-                 n.sons[a].sons[1].len == 0
+                 n.sons[a].sons[1].kind in {nkBracket, nkArgList}
         # Steal the container and pass it along
         setSon(m.call, formal.position + 1, n.sons[a].sons[1])
       else:
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index a34383d9f..fb256b895 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -18,7 +18,7 @@ const
   szIllegalRecursion* = -2
   szUncomputedSize* = -1
 
-proc computeSizeAlign(conf: ConfigRef; typ: PType): void
+proc computeSizeAlign(conf: ConfigRef; typ: PType)
 
 proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
   ## returns object alignment
@@ -49,13 +49,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
   else:
     result = 1
 
-proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
+proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
+                                      initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
   ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
   ## ``align`` maximum alignment from all sub nodes
   assert n != nil
   if n.typ != nil and n.typ.size == szIllegalRecursion:
     result.offset = szIllegalRecursion
-    result.align  = szIllegalRecursion
+    result.align = szIllegalRecursion
     return
 
   result.align = 1
@@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:
       of nkOfBranch, nkElse:
         # offset parameter cannot be known yet, it needs to know the alignment first
         let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
-
         if align == szIllegalRecursion:
-          result.offset  = szIllegalRecursion
+          result.offset = szIllegalRecursion
           result.align = szIllegalRecursion
           return
-
         if align == szUnknownSize or maxChildAlign == szUnknownSize:
           maxChildAlign = szUnknownSize
         else:
           maxChildAlign = max(maxChildAlign, align)
       else:
         internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
-
     if maxChildAlign == szUnknownSize:
       result.align  = szUnknownSize
       result.offset = szUnknownSize
     else:
       # the union neds to be aligned first, before the offsets can be assigned
       let kindUnionOffset = align(kindOffset, maxChildAlign)
-
       var maxChildOffset: BiggestInt = 0
       for i in 1 ..< sonsLen(n):
         let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
         maxChildOffset = max(maxChildOffset, offset)
-
       result.align = max(kindAlign, maxChildAlign)
       result.offset = maxChildOffset
-
-
   of nkRecList:
     result.align = 1 # maximum of all member alignments
     var offset = initialOffset
-
     for i, child in n.sons:
       let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
-
       if new_offset == szIllegalRecursion:
         result.offset = szIllegalRecursion
         result.align = szIllegalRecursion
         return
-
       elif new_offset == szUnknownSize or offset == szUnknownSize:
         # if anything is unknown, the rest becomes unknown as well
         offset = szUnknownSize
         result.align = szUnknownSize
-
       else:
         offset = new_offset
         result.align = max(result.align, align)
-
     # final alignment
     if offset == szUnknownSize:
       result.offset = szUnknownSize
     else:
       result.offset = align(offset, result.align)
-
   of nkSym:
     var size = szUnknownSize
     var align = szUnknownSize
-
     if n.sym.bitsize == 0: # 0 represents bitsize not set
       computeSizeAlign(conf, n.sym.typ)
       size = n.sym.typ.size.int
@@ -155,7 +142,6 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
     let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
     # the union neds to be aligned first, before the offsets can be assigned
     let kindUnionOffset = kindOffset
-
     var maxChildOffset: BiggestInt = kindUnionOffset
     for i in 1 ..< sonsLen(n):
       let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
@@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
       if result == szIllegalRecursion:
         break
   of nkSym:
-    computeSizeAlign(conf, n.sym.typ)
-    n.sym.offset = initialOffset.int
-    result = n.sym.offset + n.sym.typ.size
+    var size = szUnknownSize
+    if n.sym.bitsize == 0:
+      computeSizeAlign(conf, n.sym.typ)
+      size = n.sym.typ.size.int
+
+    if initialOffset == szUnknownSize or size == szUnknownSize:
+      n.sym.offset = szUnknownSize
+      result = szUnknownSize
+    else:
+      n.sym.offset = int(initialOffset)
+      result = initialOffset + n.sym.typ.size
   else:
     result = szUnknownSize
 
@@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
     var headerAlign: int16
     if typ.sons[0] != nil:
       # compute header size
-
       if conf.cmd == cmdCompileToCpp:
         # if the target is C++ the members of this type are written
         # into the padding byets at the end of the parent type. At the
@@ -364,7 +357,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.size = szUnknownSize
       typ.align = szUnknownSize
       return
-
     # header size is already in size from computeObjectOffsetsFoldFunction
     # maxAlign is probably not changed at all from headerAlign
     if tfPacked in typ.flags:
@@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
     else:
       typ.align = int16(max(align, headerAlign))
       typ.size = align(offset, typ.align)
-
   of tyInferred:
     if typ.len > 1:
       computeSizeAlign(conf, typ.lastSon)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index dfa6e5ddb..f3f960136 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -456,20 +456,27 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
 proc extractPragma(s: PSym): PNode =
   if s.kind in routineKinds:
     result = s.ast[pragmasPos]
-  elif s.kind in {skType}:
+  elif s.kind in {skType, skVar, skLet}:
     # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
     result = s.ast[0][1]
   doAssert result == nil or result.kind == nkPragma
 
 proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
-  let pragmaNode = extractPragma(s)
+  var pragmaNode: PNode
+  if optOldAst in conf.options and s.kind in {skVar, skLet}:
+    pragmaNode = nil
+  else:
+    pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s)
+  let name =
+    if s.kind == skEnumField and sfDeprecated notin s.flags: "enum '" & s.owner.name.s & "' which contains field '" & s.name.s & "'"
+    else: s.name.s
   if pragmaNode != nil:
     for it in pragmaNode:
       if whichPragma(it) == wDeprecated and it.safeLen == 2 and
           it[1].kind in {nkStrLit..nkTripleStrLit}:
-        message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s)
+        message(conf, info, warnDeprecated, it[1].strVal & "; " & name & " is deprecated")
         return
-  message(conf, info, warnDeprecated, s.name.s)
+  message(conf, info, warnDeprecated, name & " is deprecated")
 
 proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
   let pragmaNode = extractPragma(s)
@@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
   incl(s.flags, sfUsed)
   if s.kind == skEnumField and s.owner != nil:
     incl(s.owner.flags, sfUsed)
+    if sfDeprecated in s.owner.flags:
+      warnAboutDeprecated(conf, info, s)
   if {sfDeprecated, sfError} * s.flags != {}:
     if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
     if sfError in s.flags: userError(conf, info, s)
diff --git a/compiler/types.nim b/compiler/types.nim
index b163ca4e9..797336ddf 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -107,20 +107,23 @@ proc isFloatLit*(t: PType): bool {.inline.} =
   result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
 
 proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
-  result = sym.owner.name.s & '.' & sym.name.s & '('
-  var n = sym.typ.n
-  for i in countup(1, sonsLen(n) - 1):
-    let p = n.sons[i]
-    if p.kind == nkSym:
-      add(result, p.sym.name.s)
-      add(result, ": ")
-      add(result, typeToString(p.sym.typ, prefer))
-      if i != sonsLen(n)-1: add(result, ", ")
-    else:
-      result.add renderTree(p)
-  add(result, ')')
-  if n.sons[0].typ != nil:
-    result.add(": " & typeToString(n.sons[0].typ, prefer))
+  assert sym != nil
+  result = sym.owner.name.s & '.' & sym.name.s
+  if sym.kind in routineKinds:
+    result.add '('
+    var n = sym.typ.n
+    for i in countup(1, sonsLen(n) - 1):
+      let p = n.sons[i]
+      if p.kind == nkSym:
+        add(result, p.sym.name.s)
+        add(result, ": ")
+        add(result, typeToString(p.sym.typ, prefer))
+        if i != sonsLen(n)-1: add(result, ", ")
+      else:
+        result.add renderTree(p)
+    add(result, ')')
+    if n.sons[0].typ != nil:
+      result.add(": " & typeToString(n.sons[0].typ, prefer))
   result.add "[declared in "
   result.add(conf$sym.info)
   result.add "]"
@@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
        sfAnon notin t.sym.flags:
     if t.kind == tyInt and isIntLit(t):
       result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
-    elif t.kind == tyAlias:
+    elif t.kind == tyAlias and t.sons[0].kind != tyAlias:
       result = typeToString(t.sons[0])
     elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
       result = t.sym.name.s
@@ -756,7 +759,7 @@ type
 
   TTypeCmpFlags* = set[TTypeCmpFlag]
 
-  TSameTypeClosure = object {.pure.}
+  TSameTypeClosure = object
     cmp: TDistinctCompare
     recCheck: int
     flags: TTypeCmpFlags
diff --git a/compiler/unittest_light.nim b/compiler/unittest_light.nim
new file mode 100644
index 000000000..d9842b399
--- /dev/null
+++ b/compiler/unittest_light.nim
@@ -0,0 +1,38 @@
+# note: consider merging tests/assert/testhelper.nim here.
+
+proc mismatch*[T](lhs: T, rhs: T): string =
+  ## Simplified version of `unittest.require` that satisfies a common use case,
+  ## while avoiding pulling too many dependencies. On failure, diagnostic
+  ## information is provided that in particular makes it easy to spot
+  ## whitespace mismatches and where the mismatch is.
+  proc replaceInvisible(s: string): string =
+    for a in s:
+      case a
+      of '\n': result.add "\\n\n"
+      else: result.add a
+
+  proc quoted(s: string): string = result.addQuoted s
+
+  result.add "\n"
+  result.add "lhs:{" & replaceInvisible(
+      $lhs) & "}\nrhs:{" & replaceInvisible($rhs) & "}\n"
+  when compiles(lhs.len):
+    if lhs.len != rhs.len:
+      result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n"
+    when compiles(lhs[0]):
+      var i = 0
+      while i < lhs.len and i < rhs.len:
+        if lhs[i] != rhs[i]: break
+        i.inc
+      result.add "first mismatch index: " & $i & "\n"
+      if i < lhs.len and i < rhs.len:
+        result.add "lhs[i]: {" & quoted($lhs[i]) & "}\nrhs[i]: {" & quoted(
+            $rhs[i]) & "}\n"
+      result.add "lhs[0..<i]:{" & replaceInvisible($lhs[
+          0..<i]) & "}"
+
+proc assertEquals*[T](lhs: T, rhs: T) =
+  when false: # can be useful for debugging to see all that's fed to this.
+    echo "----" & $lhs
+  if lhs!=rhs:
+    doAssert false, mismatch(lhs, rhs)
diff --git a/compiler/vm.nim b/compiler/vm.nim
index c8784c3e7..10d38fe77 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -10,10 +10,6 @@
 ## This file implements the new evaluation engine for Nim code.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-const
-  debugEchoCode = false
-  traceCode = debugEchoCode
-
 import ast except getstr
 
 import
@@ -26,6 +22,9 @@ from evaltempl import evalTemplate
 
 from modulegraphs import ModuleGraph, PPassContext
 
+const
+  traceCode = debugEchoCode
+
 when hasFFI:
   import evalffi
 
@@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
       return
     stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
     var info = c.debug[pc]
-    # we now use the same format as in system/except.nim
-    var s = substr(toFilename(c.config, info), 0)
-    # this 'substr' prevents a strange corruption. XXX This needs to be
-    # investigated eventually but first attempts to fix it broke everything
-    # see the araq-wip-fixed-writebarrier branch.
+    # we now use a format similar to the one in lib/system/excpt.nim
+    var s = ""
+    # todo: factor with quotedFilename
+    if optExcessiveStackTrace in c.config.globalOptions:
+      s = toFullPath(c.config, info)
+    else:
+      s = toFilename(c.config, info)
     var line = toLinenumber(info)
+    var col = toColumn(info)
     if line > 0:
       add(s, '(')
       add(s, $line)
+      add(s, ", ")
+      add(s, $(col + ColOffset))
       add(s, ')')
     if x.prc != nil:
       for k in 1..max(1, 25-s.len): add(s, ' ')
       add(s, x.prc.name.s)
     msgWriteln(c.config, s)
 
-proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
+proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
                 msg: string, lineInfo: TLineInfo) =
   msgWriteln(c.config, "stack trace: (most recent call last)")
   stackTraceAux(c, tos, pc)
@@ -88,8 +92,14 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
   if c.mode == emRepl: globalError(c.config, lineInfo, msg)
   else: localError(c.config, lineInfo, msg)
 
-proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
-  stackTrace(c, tos, pc, msg, c.debug[pc])
+template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
+                    msg: string, lineInfo: TLineInfo) =
+  stackTraceImpl(c, tos, pc, msg, lineInfo)
+  return
+
+template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
+  stackTraceImpl(c, tos, pc, msg, c.debug[pc])
+  return
 
 proc bailOut(c: PCtx; tos: PStackFrame) =
   stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
@@ -950,13 +960,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeBC(rkInt)
       let a = regs[rb].node
       let b = regs[rc].node
-      if a.kind == nkSym and a.sym.kind in skProcKinds and 
+      if a.kind == nkSym and a.sym.kind in skProcKinds and
          b.kind == nkSym and b.sym.kind in skProcKinds:
         regs[ra].intVal =
           if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1
           else: 0
-      else:    
-        stackTrace(c, tos, pc, "node is not a proc symbol") 
+      else:
+        stackTrace(c, tos, pc, "node is not a proc symbol")
     of opcEcho:
       let rb = instr.regB
       if rb == 1:
@@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let newLen = regs[rb].intVal.int
       if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
       else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
-    of opcReset:
-      internalError(c.config, c.debug[pc], "too implement")
     of opcNarrowS:
       decodeB(rkInt)
       let min = -(1.BiggestInt shl (rb-1))
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 493078f74..a43f8dbba 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -73,7 +73,7 @@ type
     opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
     opcIsNil, opcOf, opcIs,
     opcSubStr, opcParseFloat, opcConv, opcCast,
-    opcQuit, opcReset,
+    opcQuit,
     opcNarrowS, opcNarrowU,
 
     opcAddStrCh,
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 1f2a3e6d1..e7993dfb2 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -33,6 +33,11 @@ import
 import platform
 from os import splitFile
 
+const
+  debugEchoCode* = defined(nimVMDebug)
+
+when debugEchoCode:
+  import asciitables
 when hasFFI:
   import evalffi
 
@@ -43,9 +48,10 @@ type
   TGenFlags = set[TGenFlag]
 
 proc debugInfo(c: PCtx; info: TLineInfo): string =
-  result = toFilename(c.config, info).splitFile.name & ":" & $info.line
+  result = toFileLineCol(c.config, info)
 
 proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
+  ## for debugging purposes
   # first iteration: compute all necessary labels:
   var jumpTargets = initIntSet()
   let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1)
@@ -54,7 +60,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
     if x.opcode in relativeJumps:
       jumpTargets.incl(i+x.regBx-wordExcess)
 
-  # for debugging purposes
+  template toStr(opc: TOpcode): string = ($opc).substr(3)
+
+  result.add "code listing:\n"
   var i = start
   while i <= last:
     if i in jumpTargets: result.addf("L$1:\n", i)
@@ -62,34 +70,39 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
 
     result.add($i)
     let opc = opcode(x)
-    if opc in {opcConv, opcCast}:
+    if opc in {opcIndCall, opcIndCallAsgn}:
+      result.addf("\t$#\tr$#, r$#, nargs:$#", opc.toStr, x.regA,
+                  x.regB, x.regC)
+    elif opc in {opcConv, opcCast}:
       let y = c.code[i+1]
       let z = c.code[i+2]
-      result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB,
+      result.addf("\t$#\tr$#, r$#, $#, $#", opc.toStr, x.regA, x.regB,
         c.types[y.regBx-wordExcess].typeToString,
         c.types[z.regBx-wordExcess].typeToString)
       inc i, 2
     elif opc < firstABxInstr:
-      result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
+      result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA,
                   x.regB, x.regC)
     elif opc in relativeJumps:
-      result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
+      result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA,
                   i+x.regBx-wordExcess)
     elif opc in {opcLdConst, opcAsgnConst}:
       let idx = x.regBx-wordExcess
-      result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
+      result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA,
         c.constants[idx].renderTree, $idx)
     elif opc in {opcMarshalLoad, opcMarshalStore}:
       let y = c.code[i+1]
-      result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
+      result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB,
         c.types[y.regBx-wordExcess].typeToString)
       inc i
     else:
-      result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
+      result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess)
     result.add("\t#")
     result.add(debugInfo(c, c.debug[i]))
     result.add("\n")
     inc i
+  when debugEchoCode:
+    result = result.alignTable
 
 proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
   var buf = ""
@@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mReset:
     unused(c, n, dest)
     var d = c.genx(n.sons[1])
-    c.gABC(n, opcReset, d)
+    c.gABx(n, opcLdNull, d, c.genType(n.sons[1].typ))
+    c.gABx(n, opcNodeToReg, d, d)
+    c.genAsgnPatch(n.sons[1], d)
   of mOf, mIs:
     if dest < 0: dest = c.getTemp(n.typ)
     var tmp = c.genx(n.sons[1])
@@ -1736,8 +1751,9 @@ proc genVarSection(c: PCtx; n: PNode) =
     #assert(a.sons[0].kind == nkSym) can happen for transformed vars
     if a.kind == nkVarTuple:
       for i in 0 .. a.len-3:
-        if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
-        checkCanEval(c, a[i])
+        if a[i].kind == nkSym:
+          if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
+          checkCanEval(c, a[i])
       c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
     elif a.sons[0].kind == nkSym:
       let s = a.sons[0].sym
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 75873bfe8..56c97dec6 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
   arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
   floor, ceil, `mod`
 
-from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir
+from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename
 
 template mathop(op) {.dirty.} =
   registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
@@ -120,3 +120,6 @@ proc registerAdditionalOps*(c: PCtx) =
       setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
     systemop gorgeEx
   macrosop getProjectPath
+
+  registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
+    setResult(a, getAppFilename())
diff --git a/config/config.nims b/config/config.nims
new file mode 100644
index 000000000..3f0d9e1b8
--- /dev/null
+++ b/config/config.nims
@@ -0,0 +1 @@
+# empty config.nims to prevent future regressions, see #9990
diff --git a/config/nim.cfg b/config/nim.cfg
index 38683b304..2a118c5cf 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -102,9 +102,6 @@ path="$lib/pure"
     tlsEmulation:on
   @end
   @if haiku:
-    # Haiku currently have problems with TLS
-    # https://dev.haiku-os.org/ticket/14342
-    tlsEmulation:on
     gcc.options.linker = "-Wl,--as-needed -lnetwork"
     gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
     clang.options.linker = "-Wl,--as-needed -lnetwork"
@@ -220,10 +217,25 @@ llvm_gcc.options.size = "-Os"
 
 # Configuration for the LLVM CLang compiler:
 clang.options.debug = "-g"
+clang.cpp.options.debug = "-g"
 clang.options.always = "-w"
 clang.options.speed = "-O3"
 clang.options.size = "-Os"
 
+@if windows:
+  clang_cl.cpp.options.always %=  "${clang_cl.options.always} /EHsc"
+  @if not release:
+    clang_cl.options.linker = "/Z7"
+    clang_cl.cpp.options.linker = "/Z7"
+  @end
+  clang.options.debug = "-g -gcodeview"
+  clang.cpp.options.debug = "-g -gcodeview"
+  @if not release:
+    clang.options.linker = "-g"
+    clang.cpp.options.linker = "-g"
+  @end
+@end
+
 # Configuration for the Visual C/C++ compiler:
 # VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
 # before calling the compiler.
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 60cae7fd0..7ab85abfc 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -18,9 +18,9 @@ Advanced commands:
 
 Advanced options:
   -o:FILE, --out:FILE       set the output filename
-  --stdout                  output to stdout
+  --stdout:on|off           output to stdout
   --colors:on|off           turn compiler messages coloring on|off
-  --listFullPaths           list full paths in messages
+  --listFullPaths:on|off    list full paths in messages
   -w:on|off|list, --warnings:on|off|list
                             turn all warnings on|off or list all available
   --warning[X]:on|off       turn specific warning X on|off
@@ -39,29 +39,27 @@ Advanced options:
   --nimcache:PATH           set the path used for generated files
   --header:FILE             the compiler should produce a .h file (FILE
                             is optional)
-  -c, --compileOnly         compile Nim files only; do not assemble or link
-  --noLinking               compile Nim and generated files but do not link
-  --noMain                  do not generate a main procedure
-  --genScript               generate a compile script (in the 'nimcache'
+  -c, --compileOnly:on|off  compile Nim files only; do not assemble or link
+  --noLinking:on|off        compile Nim and generated files but do not link
+  --noMain:on|off           do not generate a main procedure
+  --genScript:on|off        generate a compile script (in the 'nimcache'
                             subdirectory named 'compile_$$project$$scriptext'),
                             implies --compileOnly
-  --genDeps                 generate a '.deps' file containing the dependencies
+  --genDeps:on|off          generate a '.deps' file containing the dependencies
   --os:SYMBOL               set the target operating system (cross-compilation)
   --cpu:SYMBOL              set the target processor (cross-compilation)
-  --debuginfo               enables debug information
+  --debuginfo:on|off        enables debug information
   -t, --passC:OPTION        pass an option to the C compiler
   -l, --passL:OPTION        pass an option to the linker
   --cincludes:DIR           modify the C compiler header search path
   --clibdir:DIR             modify the linker library search path
   --clib:LIBNAME            link an additional C library
                             (you should omit platform-specific extensions)
-  --genMapping              generate a mapping file containing
-                            (Nim, mangled) identifier pairs
   --project                 document the whole project (doc2)
   --docSeeSrcUrl:url        activate 'see source' for doc and doc2 commands
                             (see doc.item.seesrc in config/nimdoc.cfg)
   --lineDir:on|off          generation of #line directive on|off
-  --embedsrc                embeds the original source code as comments
+  --embedsrc:on|off         embeds the original source code as comments
                             in the generated output
   --threadanalysis:on|off   turn thread analysis on|off
   --tlsEmulation:on|off     turn thread local storage emulation on|off
@@ -78,11 +76,12 @@ Advanced options:
                             strings is allowed; only for backwards compatibility
   --nilseqs:on|off          allow 'nil' for strings/seqs for
                             backwards compatibility
-  --skipCfg                 do not read the general configuration file
-  --skipUserCfg             do not read the user's configuration file
-  --skipParentCfg           do not read the parent dirs' configuration files
-  --skipProjCfg             do not read the project's configuration file
-  --gc:refc|v2|markAndSweep|boehm|go|none|regions
+  --oldast:on|off           use old AST for backwards compatibility
+  --skipCfg:on|off          do not read the nim installation's configuration file
+  --skipUserCfg:on|off      do not read the user's configuration file
+  --skipParentCfg:on|off    do not read the parent dirs' configuration files
+  --skipProjCfg:on|off      do not read the project's configuration file
+  --gc:refc|markAndSweep|boehm|go|none|regions
                             select the GC to use; default is 'refc'
   --index:on|off            turn index file generation on|off
   --putenv:key=value        set an environment variable
@@ -98,8 +97,8 @@ Advanced options:
                             symbol matching is fuzzy so
                             that --dynlibOverride:lua matches
                             dynlib: "liblua.so.3"
-  --dynlibOverrideAll       makes the dynlib pragma have no effect
-  --listCmd                 list the commands used to execute external programs
+  --dynlibOverrideAll:on|off makes the dynlib pragma have no effect
+  --listCmd:on|off          list the commands used to execute external programs
   --parallelBuild:0|1|...   perform a parallel build
                             value = number of processors (0 for auto-detect)
   --incremental:on|off      only recompile the changed modules (experimental!)
@@ -108,3 +107,6 @@ Advanced options:
   --experimental:$1
                             enable experimental language feature
   -v, --version             show detailed version information
+  --profiler:on|off         Enable profiling; requires `import nimprof`, and
+                            works better with `--stackTrace:on`
+                            see also https://nim-lang.github.io/Nim/estp.html
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index a9166d36c..96c9ced3d 100644
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -15,7 +15,7 @@ Options:
                             (Optionally: Define the value for that symbol,
                             see: "compile time define pragmas")
   -u, --undef:SYMBOL        undefine a conditional symbol
-  -f, --forceBuild          force rebuilding of all modules
+  -f, --forceBuild:on|off   force rebuilding of all modules
   --stackTrace:on|off       turn stack tracing on|off
   --lineTrace:on|off        turn line tracing on|off
   --threads:on|off          turn support for multi-threading on|off
@@ -35,7 +35,7 @@ Options:
   --debugger:native|endb    use native debugger (gdb) | ENDB (experimental)
   --app:console|gui|lib|staticlib
                             generate a console app|GUI app|DLL|static library
-  -r, --run                 run the compiled program with given arguments
+  -r, --run:on|off          run the compiled program with given arguments
   --fullhelp                show all command line switches
   -h, --help                show this help
 
diff --git a/doc/docs.rst b/doc/docs.rst
index cd1a05853..4a69bd69a 100644
--- a/doc/docs.rst
+++ b/doc/docs.rst
@@ -6,6 +6,9 @@ The documentation consists of several documents:
 - | `Tutorial (part II) <tut2.html>`_
   | The Nim tutorial part two deals with the advanced language constructs.
 
+- | `Tutorial (part III) <tut3.html>`_
+  | The Nim tutorial part three about Nim's macro system.
+
 - | `Language Manual <manual.html>`_
   | The Nim manual is a draft that will evolve into a proper specification.
 
diff --git a/doc/lib.rst b/doc/lib.rst
index 89e3cca40..1f19f9bf4 100644
--- a/doc/lib.rst
+++ b/doc/lib.rst
@@ -107,8 +107,8 @@ String handling
   substrings, replacing substrings.
 
 * `strformat <strformat.html>`_
-  Macro based standard string interpolation / formatting. Inpired by
-  Python's ```f``-strings.
+  Macro based standard string interpolation / formatting. Inspired by
+  Python's ``f``-strings.
 
 * `strmisc <strmisc.html>`_
   This module contains uncommon string handling operations that do not
@@ -141,12 +141,6 @@ String handling
   Ropes can represent very long strings efficiently; especially concatenation
   is done in O(1) instead of O(n).
 
-* `matchers <matchers.html>`_
-  This module contains various string matchers for email addresses, etc.
-
-* `subexes <subexes.html>`_
-  This module implements advanced string substitution operations.
-
 * `std/editdistance <editdistance.html>`_
   This module contains an algorithm to compute the edit distance between two
   Unicode strings.
@@ -236,9 +230,6 @@ Internet Protocols and Support
 * `cgi <cgi.html>`_
   This module implements helpers for CGI applications.
 
-* `scgi <scgi.html>`_
-  This module implements helpers for SCGI applications.
-
 * `browsers <browsers.html>`_
   This module implements procs for opening URLs with the user's default
   browser.
@@ -275,8 +266,8 @@ Internet Protocols and Support
   module.
 
 * `net <net.html>`_
-  This module implements a high-level sockets API. It will replace the
-  ``sockets`` module in the future.
+  This module implements a high-level sockets API. It replaces the
+  ``sockets`` module.
 
 * `nativesockets <nativesockets.html>`_
   This module implements a low-level sockets API.
@@ -456,13 +447,6 @@ Database support
   for other databases too.
 
 
-Other
------
-
-* `ssl <ssl.html>`_
-  This module provides an easy to use sockets-style
-  Nim interface to the OpenSSL library.
-
 
 Wrappers
 ========
diff --git a/doc/manual.rst b/doc/manual.rst
index a646b7963..09b9b4d78 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -480,7 +480,6 @@ The type suffixes are:
   ``'d``             float64
   ``'f32``           float32
   ``'f64``           float64
-  ``'f128``          float128
 =================    =========================
 
 Floating point literals may also be in binary, octal or hexadecimal
@@ -1587,7 +1586,7 @@ details like this when mixing garbage collected data with unmanaged memory.
 Not nil annotation
 ------------------
 
-All types for that ``nil`` is a valid value can be annotated to
+All types for which ``nil`` is a valid value can be annotated to
 exclude ``nil`` as a valid value with the ``not nil`` annotation:
 
 .. code-block:: nim
@@ -1664,12 +1663,12 @@ Nim supports these `calling conventions`:idx:\:
     and another one for the pointer to implicitly passed environment.
 
 `stdcall`:idx:
-    This the stdcall convention as specified by Microsoft. The generated C
+    This is the stdcall convention as specified by Microsoft. The generated C
     procedure is declared with the ``__stdcall`` keyword.
 
 `cdecl`:idx:
     The cdecl convention means that a procedure shall use the same convention
-    as the C compiler. Under windows the generated C procedure is declared with
+    as the C compiler. Under Windows the generated C procedure is declared with
     the ``__cdecl`` keyword.
 
 `safecall`:idx:
@@ -7365,6 +7364,17 @@ Example:
 
   embedsC()
 
+``nimbase.h`` defines ``NIM_EXTERNC`` C macro that can be used for
+``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, eg:
+
+.. code-block:: Nim
+  proc foobar() {.importc:"$1".}
+  {.emit: """
+  #include <stdio.h>
+  NIM_EXTERNC
+  void fun(){}
+  """.}
+
 For backwards compatibility, if the argument to the ``emit`` statement
 is a single string literal, Nim symbols can be referred to via backticks.
 This usage is however deprecated.
diff --git a/doc/nimc.rst b/doc/nimc.rst
index e1bf98ece..4ffb595c0 100644
--- a/doc/nimc.rst
+++ b/doc/nimc.rst
@@ -347,13 +347,16 @@ complete list.
 Define                   Effect
 ======================   =========================================================
 ``release``              Turns off runtime checks and turns on the optimizer.
+                         More aggressive optimizations are possible, eg:
+                         ``--passC:-ffast-math`` (but see issue #10305)
+                         ``--stacktrace:off``
 ``useWinAnsi``           Modules like ``os`` and ``osproc`` use the Ansi versions
                          of the Windows API. The default build uses the Unicode
                          version.
 ``useFork``              Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
 ``useNimRtl``            Compile and link against ``nimrtl.dll``.
 ``useMalloc``            Makes Nim use C's `malloc`:idx: instead of Nim's
-                         own memory manager, ableit prefixing each allocation with
+                         own memory manager, albeit prefixing each allocation with
                          its size to support clearing memory on reallocation.
                          This only works with ``gc:none``.
 ``useRealtimeGC``        Enables support of Nim's GC for *soft* realtime
diff --git a/doc/nims.rst b/doc/nims.rst
index 034ad1fda..eaf20a6db 100644
--- a/doc/nims.rst
+++ b/doc/nims.rst
@@ -26,9 +26,28 @@ previous settings):
    ``$project.nim``. This file can be skipped with the same
    ``--skipProjCfg`` command line option.
 
-The VM cannot deal with ``importc`` because the FFI is not
-available. So the stdlib modules using ``importc`` cannot be used with
-Nim's VM. However, at least the following modules are available:
+Limitations
+=================================
+
+NimScript is subject to some limitations caused by the implementation of the VM
+(virtual machine):
+
+* Nim's FFI (foreign function interface) is not available in NimScript. This
+  means that any stdlib module which relies on ``importc`` can not be used in
+  the VM.
+
+* ``ptr`` operations are are hard to emulate with the symbolic representation
+  the VM uses. They are available and tested extensively but there are bugs left.
+
+* ``var T`` function arguments rely on ``ptr`` operations internally and might
+  also be problematic in some cases.
+
+* More than one level of `ref` is generally not supported (for example, the type 
+  `ref ref int`).
+
+* multimethods are not available.
+
+Given the above restrictions, at least the following modules are available:
 
 * `macros <macros.html>`_
 * `os <os.html>`_
@@ -98,17 +117,6 @@ Task          Description
 =========     ===================================================
 
 
-If the task runs an external command via ``exec`` it should afterwards call
-``setCommand "nop"`` to tell the Nim compiler that nothing else needs to be
-done:
-
-.. code-block:: nim
-
-  task tests, "test regular expressions":
-    exec "nim c -r tests"
-    setCommand "nop"
-
-
 Look at the module `distros <distros.html>`_ for some support of the
 OS's native package managers.
 
diff --git a/koch.nim b/koch.nim
index 9596ce21f..f70cf2142 100644
--- a/koch.nim
+++ b/koch.nim
@@ -46,6 +46,8 @@ Possible Commands:
   boot [options]           bootstraps with given command line options
   distrohelper [bindir]    helper for distro packagers
   tools                    builds Nim related tools
+  toolsNoNimble            builds Nim related tools (except nimble)
+                           doesn't require network connectivity
   nimble                   builds the Nimble tool
 Boot options:
   -d:release               produce a release version of the compiler
@@ -56,6 +58,7 @@ Boot options:
                            for bootstrapping
 
 Commands for core developers:
+  runCI                    runs continuous integration (CI), eg from travis
   docs [options]           generates the full documentation
   csource -d:release       builds the C sources for installation
   pdf                      builds the PDF documentation
@@ -71,6 +74,12 @@ Web options:
                            build the official docs, use UA-48159761-1
 """
 
+let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed eg: `nim c -o:koch_debug koch.nim`
+               else: getAppDir() / "koch".exe # works for winrelease
+
+proc kochExec*(cmd: string) =
+  exec kochExe.quoteShell & " " & cmd
+
 template withDir(dir, body) =
   let old = getCurrentDir()
   try:
@@ -121,9 +130,6 @@ proc bundleNimbleExe(latest: bool) =
   # installer.ini expects it under $nim/bin
   nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
 
-proc buildNimfind() =
-  nimCompile("tools/nimfind.nim", options = "-d:release")
-
 proc buildNimble(latest: bool) =
   # old installations created nim/nimblepkg/*.nim files. We remove these
   # here so that it cannot cause problems (nimble bug #306):
@@ -197,13 +203,12 @@ proc buildTool(toolname, args: string) =
   nimexec("cc $# $#" % [args, toolname])
   copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
 
-proc buildTools(latest: bool) =
+proc buildTools() =
   bundleNimsuggest()
   nimCompile("tools/nimgrep.nim", options = "-d:release")
   when defined(windows): buildVccTool()
   nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
-  buildNimble(latest)
-  buildNimfind()
+  nimCompile("tools/nimfind.nim", options = "-d:release")
 
 proc nsis(latest: bool; args: string) =
   bundleNimbleExe(latest)
@@ -272,16 +277,30 @@ proc boot(args: string) =
   var output = "compiler" / "nim".exe
   var finalDest = "bin" / "nim".exe
   # default to use the 'c' command:
-  let defaultCommand = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": "cpp" else: "c"
-  let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
+  let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
   let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
                       hostOs & "_" & hostCpu
 
-  copyExe(findStartNim(), 0.thVersion)
+  let nimStart = findStartNim()
+  copyExe(nimStart, 0.thVersion)
   for i in 0..2:
+    let defaultCommand = if useCpp: "cpp" else: "c"
+    let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
     echo "iteration: ", i+1
-    exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
-        smartNimcache]
+    var extraOption = ""
+    if i == 0:
+      extraOption.add " --skipUserCfg --skipParentCfg"
+        # Note(D20190115T162028:here): the configs are skipped for bootstrap
+        # (1st iteration) to prevent newer flags from breaking bootstrap phase.
+        # fixes #10030.
+      let ret = execCmdEx(nimStart & " --version")
+      doAssert ret.exitCode == 0
+      let version = ret.output.splitLines[0]
+      if version.startsWith "Nim Compiler Version 0.19.0":
+        extraOption.add " -d:nimBoostrapCsources0_19_0"
+        # remove this when csources get updated
+    exec i.thVersion & " $# $# $# --nimcache:$# compiler" / "nim.nim" %
+      [bootOptions, extraOption, args, smartNimcache]
     if sameFileContent(output, i.thVersion):
       copyExe(output, finalDest)
       echo "executables are equal: SUCCESS!"
@@ -352,8 +371,8 @@ proc winReleaseArch(arch: string) =
     # Rebuilding koch is necessary because it uses its pointer size to
     # determine which mingw link to put in the NSIS installer.
     nimexec "c --cpu:$# koch" % cpu
-    exec "koch boot -d:release --cpu:$#" % cpu
-    exec "koch --latest zip -d:release"
+    kochExec "boot -d:release --cpu:$#" % cpu
+    kochExec "--latest zip -d:release"
     overwriteFile r"build\nim-$#.zip" % VersionAsString,
              r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
 
@@ -428,6 +447,47 @@ proc xtemp(cmd: string) =
   finally:
     copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
 
+proc runCI(cmd: string) =
+  doAssert cmd.len == 0, cmd # avoid silently ignoring
+  echo "runCI:", cmd
+  # note(@araq): Do not replace these commands with direct calls (eg boot())
+  # as that would weaken our testing efforts.
+  when defined(posix): # appveyor (on windows) didn't run this
+    kochExec "boot"
+  kochExec "boot -d:release"
+
+  ## build nimble early on to enable remainder to depend on it if needed
+  kochExec "nimble"
+
+  when false:
+    for pkg in "zip opengl sdl1 jester@#head niminst".split:
+      exec "nimble install -y" & pkg
+
+  buildTools() # altenatively, kochExec "tools --toolsNoNimble"
+
+  ## run tests
+  exec "nim e tests/test_nimscript.nims"
+  when defined(windows):
+    # note: will be over-written below
+    exec "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester"
+
+  # main bottleneck here
+  exec "nim c -r -d:nimCoroutines testament/tester --pedantic all -d:nimCoroutines"
+
+  exec "nim c -r nimdoc/tester"
+  exec "nim c -r nimpretty/tester.nim"
+  when defined(posix):
+    exec "nim c -r nimsuggest/tester"
+
+  ## remaining actions
+  when defined(posix):
+    kochExec "docs --git.commit:devel"
+    kochExec "csource"
+  elif defined(windows):
+    when false:
+      kochExec "csource"
+      kochExec "zip"
+
 proc pushCsources() =
   if not dirExists("../csources/.git"):
     quit "[Error] no csources git repository found"
@@ -475,8 +535,7 @@ proc testUnixInstall(cmdLineRest: string) =
       execCleanPath("./koch --latest tools")
       # check the tests work:
       putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
-      execCleanPath("./koch tests", destDir / "bin")
-      #execCleanPath("./koch tests cat newconfig", destDir / "bin")
+      execCleanPath("./koch tests cat megatest", destDir / "bin")
     else:
       echo "Version check: failure"
   finally:
@@ -512,6 +571,10 @@ when isMainModule:
   var op = initOptParser()
   var latest = false
   var stable = false
+  template isLatest(): bool =
+    if stable: false
+    else:
+      existsDir(".git") or latest
   while true:
     op.next()
     case op.kind
@@ -537,17 +600,18 @@ when isMainModule:
       of "distrohelper": geninstall()
       of "install": install(op.cmdLineRest)
       of "testinstall": testUnixInstall(op.cmdLineRest)
+      of "runci": runCI(op.cmdLineRest)
       of "test", "tests": tests(op.cmdLineRest)
       of "temp": temp(op.cmdLineRest)
       of "xtemp": xtemp(op.cmdLineRest)
       of "wintools": bundleWinTools()
-      of "nimble":
-        if stable: buildNimble(false)
-        else: buildNimble(existsDir(".git") or latest)
+      of "nimble": buildNimble(isLatest())
       of "nimsuggest": bundleNimsuggest()
+      of "toolsnonimble":
+        buildTools()
       of "tools":
-        if stable: buildTools(false)
-        else: buildTools(existsDir(".git") or latest)
+        buildTools()
+        buildNimble(isLatest())
       of "pushcsource", "pushcsources": pushCsources()
       of "valgrind": valgrind(op.cmdLineRest)
       else: showHelp()
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index f45ca3f82..3a85324ba 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -151,7 +151,7 @@ proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
 
 proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
   ## compares two Nim symbols
-  ## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
+  ## **Deprecated since version 0.18.1**; Use ``==(NimNode, NimNode)`` instead.
 
 
 proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
@@ -277,9 +277,9 @@ when defined(nimHasSymOwnerInMacro):
 when defined(nimHasInstantiationOfInMacro):
   proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.}
     ## check if proc symbol is instance of the generic proc symbol
-    ## useful to check proc symbols against generic symbols 
+    ## useful to check proc symbols against generic symbols
     ## returned by `bindSym`
- 
+
 proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
   ## with 'getType' you can access the node's `type`:idx:. A Nim type is
   ## mapped to a Nim AST too, so it's slightly confusing but it means the same
@@ -377,7 +377,9 @@ proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
 proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
 
 proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
-  ## writes an error message at compile time
+  ## writes an error message at compile time. The optional ``n: NimNode``
+  ## parameter is used as the source for file and line number information in
+  ## the compilation error message.
 
 proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
   ## writes a warning message at compile time
@@ -1402,8 +1404,14 @@ proc customPragmaNode(n: NimNode): NimNode =
     let impl = n.getImpl()
     if impl.kind in RoutineNodes:
       return impl.pragma
+    elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
+      return impl[0][1]
     else:
-      return typ.getImpl()[0][1]
+      let timpl = typ.getImpl()
+      if timpl.len>0 and timpl[0].len>1:
+        return timpl[0][1]
+      else:
+        return timpl
 
   if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
     let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
@@ -1492,9 +1500,18 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   let pragmaNode = customPragmaNode(n)
   for p in pragmaNode:
     if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
-      return p[1]
-
-  error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
+      if p.len == 2:
+        result = p[1]
+      else:
+        let def = p[0].getImpl[3]
+        result = newTree(nnkPar)
+        for i in 1..<p.len:
+          let key = def[i][0]
+          let val = p[i]
+          result.add newTree(nnkExprColonExpr, key, val)
+      break
+  if result.kind == nnkEmpty:
+    error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
 
 
 when not defined(booting):
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index a41ef10ab..977b23b26 100644
--- a/lib/core/seqs.nim
+++ b/lib/core/seqs.nim
@@ -70,12 +70,6 @@ proc `=sink`[T](x: var seq[T]; y: seq[T]) =
   a.len = b.len
   a.p = b.p
 
-when false:
-  proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
-  proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
-      compilerRtl.}
-  proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
-
 
 type
   PayloadBase = object
diff --git a/lib/deprecated/core/unsigned.nim b/lib/deprecated/core/unsigned.nim
deleted file mode 100644
index 93a29e1c9..000000000
--- a/lib/deprecated/core/unsigned.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## **Warning:** Since version 0.11.4 this module is deprecated.
-##
-## This module implemented basic arithmetic operators for unsigned integers.
-## These operators are now available in the ``system`` module directly.
-
-{.deprecated.}
-
-export `shr`, `shl`, `and`, `or`, `xor`, `==`, `+`, `-`, `*`, `div`, `mod`,
-  `<=`, `<`
diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim
deleted file mode 100644
index 77c67a3e4..000000000
--- a/lib/deprecated/pure/actors.nim
+++ /dev/null
@@ -1,239 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## `Actor`:idx: support for Nim. An actor is implemented as a thread with
-## a channel as its inbox. This module requires the ``--threads:on``
-## command line switch.
-##
-## Example:
-##
-## .. code-block:: nim
-##
-##      var
-##        a: ActorPool[int, void]
-##      createActorPool(a)
-##      for i in 0 ..< 300:
-##        a.spawn(i, proc (x: int) {.thread.} = echo x)
-##      a.join()
-##
-## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
-## instead.
-
-{.deprecated.}
-
-from os import sleep
-
-type
-  Task*[In, Out] = object{.pure, final.} ## a task
-    when Out isnot void:
-      receiver*: ptr Channel[Out] ## the receiver channel of the response
-    action*: proc (x: In): Out {.thread.} ## action to execute;
-                                            ## sometimes useful
-    shutDown*: bool ## set to tell an actor to shut-down
-    data*: In ## the data to process
-
-  Actor[In, Out] = object{.pure, final.}
-    i: Channel[Task[In, Out]]
-    t: Thread[ptr Actor[In, Out]]
-
-  PActor*[In, Out] = ptr Actor[In, Out] ## an actor
-
-proc spawn*[In, Out](action: proc(
-    self: PActor[In, Out]){.thread.}): PActor[In, Out] =
-  ## creates an actor; that is a thread with an inbox. The caller MUST call
-  ## ``join`` because that also frees the actor's associated resources.
-  result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
-  open(result.i)
-  createThread(result.t, action, result)
-
-proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
-  ## gets a pointer to the associated inbox of the actor `self`.
-  result = addr(self.i)
-
-proc running*[In, Out](a: PActor[In, Out]): bool =
-  ## returns true if the actor `a` is running.
-  result = running(a.t)
-
-proc ready*[In, Out](a: PActor[In, Out]): bool =
-  ## returns true if the actor `a` is ready to process new messages.
-  result = ready(a.i)
-
-proc join*[In, Out](a: PActor[In, Out]) =
-  ## joins an actor.
-  joinThread(a.t)
-  close(a.i)
-  deallocShared(a)
-
-proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
-  ## receives a task from `a`'s inbox.
-  result = recv(a.i)
-
-proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
-                            sender: PActor[X, Y]) =
-  ## sends a message to `a`'s inbox.
-  var t: Task[In, Out]
-  t.receiver = addr(sender.i)
-  shallowCopy(t.data, msg)
-  send(receiver.i, t)
-
-proc send*[In, Out](receiver: PActor[In, Out], msg: In,
-                      sender: ptr Channel[Out] = nil) =
-  ## sends a message to `receiver`'s inbox.
-  var t: Task[In, Out]
-  t.receiver = sender
-  shallowCopy(t.data, msg)
-  send(receiver.i, t)
-
-proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
-  ## send a shutdown message to `receiver`.
-  var t: Task[In, Out]
-  t.shutdown = true
-  send(receiver.i, t)
-
-proc reply*[In, Out](t: Task[In, Out], m: Out) =
-  ## sends a message to io's output message box.
-  when Out is void:
-    {.error: "you cannot reply to a void outbox".}
-  assert t.receiver != nil
-  send(t.receiver[], m)
-
-
-# ----------------- actor pools ----------------------------------------------
-
-type
-  ActorPool*[In, Out] = object{.pure, final.}  ## an actor pool
-    actors: seq[PActor[In, Out]]
-    when Out isnot void:
-      outputs: Channel[Out]
-
-proc `^`*[T](f: ptr Channel[T]): T =
-  ## alias for 'recv'.
-  result = recv(f[])
-
-proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
-  while true:
-    var m = self.recv
-    if m.shutDown: break
-    when Out is void:
-      m.action(m.data)
-    else:
-      send(m.receiver[], m.action(m.data))
-      #self.reply()
-
-proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
-  ## creates an actor pool.
-  newSeq(a.actors, poolSize)
-  when Out isnot void:
-    open(a.outputs)
-  for i in 0 ..< a.actors.len:
-    a.actors[i] = spawn(poolWorker[In, Out])
-
-proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
-  ## waits for every actor of `a` to finish with its work. Currently this is
-  ## implemented as polling every `polling` ms and has a slight chance
-  ## of failing since we check for every actor to be in `ready` state and not
-  ## for messages still in ether. This will change in a later
-  ## version, however.
-  var allReadyCount = 0
-  while true:
-    var wait = false
-    for i in 0..high(a.actors):
-      if not a.actors[i].i.ready:
-        wait = true
-        allReadyCount = 0
-        break
-    if not wait:
-      # it's possible that some actor sent a message to some other actor but
-      # both appeared to be non-working as the message takes some time to
-      # arrive. We assume that this won't take longer than `polling` and
-      # simply attempt a second time and declare victory then. ;-)
-      inc allReadyCount
-      if allReadyCount > 1: break
-    sleep(polling)
-
-proc terminate*[In, Out](a: var ActorPool[In, Out]) =
-  ## terminates each actor in the actor pool `a` and frees the
-  ## resources attached to `a`.
-  var t: Task[In, Out]
-  t.shutdown = true
-  for i in 0..<a.actors.len: send(a.actors[i].i, t)
-  for i in 0..<a.actors.len: join(a.actors[i])
-  when Out isnot void:
-    close(a.outputs)
-  a.actors = @[]
-
-proc join*[In, Out](a: var ActorPool[In, Out]) =
-  ## short-cut for `sync` and then `terminate`.
-  sync(a)
-  terminate(a)
-
-template setupTask =
-  t.action = action
-  shallowCopy(t.data, input)
-
-template schedule =
-  # extremely simple scheduler: We always try the first thread first, so that
-  # it remains 'hot' ;-). Round-robin hurts for keeping threads hot.
-  for i in 0..high(p.actors):
-    if p.actors[i].i.ready:
-      p.actors[i].i.send(t)
-      return
-  # no thread ready :-( --> send message to the thread which has the least
-  # messages pending:
-  var minIdx = -1
-  var minVal = high(int)
-  for i in 0..high(p.actors):
-    var curr = p.actors[i].i.peek
-    if curr == 0:
-      # ok, is ready now:
-      p.actors[i].i.send(t)
-      return
-    if curr < minVal and curr >= 0:
-      minVal = curr
-      minIdx = i
-  if minIdx >= 0:
-    p.actors[minIdx].i.send(t)
-  else:
-    raise newException(DeadThreadError, "cannot send message; thread died")
-
-proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
-                       action: proc (input: In): Out {.thread.}
-                       ): ptr Channel[Out] =
-  ## uses the actor pool to run ``action(input)`` concurrently.
-  ## `spawn` is guaranteed to not block.
-  var t: Task[In, Out]
-  setupTask()
-  result = addr(p.outputs)
-  t.receiver = result
-  schedule()
-
-proc spawn*[In](p: var ActorPool[In, void], input: In,
-                 action: proc (input: In) {.thread.}) =
-  ## uses the actor pool to run ``action(input)`` concurrently.
-  ## `spawn` is guaranteed to not block.
-  var t: Task[In, void]
-  setupTask()
-  schedule()
-
-when not defined(testing) and isMainModule:
-  var
-    a: ActorPool[int, void]
-  createActorPool(a)
-  for i in 0 ..< 300:
-    a.spawn(i, proc (x: int) {.thread.} = echo x)
-
-  when false:
-    proc treeDepth(n: PNode): int {.thread.} =
-      var x = a.spawn(treeDepth, n.le)
-      var y = a.spawn(treeDepth, n.ri)
-      result = max(^x, ^y) + 1
-
-  a.join()
-
-
diff --git a/lib/deprecated/pure/actors.nim.cfg b/lib/deprecated/pure/actors.nim.cfg
deleted file mode 100644
index c6bb9c545..000000000
--- a/lib/deprecated/pure/actors.nim.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-# to shut up the tester:
---threads:on
-
diff --git a/lib/deprecated/pure/ftpclient.nim b/lib/deprecated/pure/ftpclient.nim
index d70f9556a..206c21f27 100644
--- a/lib/deprecated/pure/ftpclient.nim
+++ b/lib/deprecated/pure/ftpclient.nim
@@ -81,7 +81,6 @@ type
       oneSecond: BiggestInt # Bytes transferred in one second.
       lastProgressReport: float # Time
       toStore: string # Data left to upload (Only used with async)
-    else: nil
 
   FtpClientObj* = FtpBaseObj[Socket]
   FtpClient* = ref FtpClientObj
diff --git a/lib/deprecated/pure/parseurl.nim b/lib/deprecated/pure/parseurl.nim
deleted file mode 100644
index b19f6dc85..000000000
--- a/lib/deprecated/pure/parseurl.nim
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## **Warnings:** This module is deprecated since version 0.10.2.
-## Use the `uri <uri.html>`_ module instead.
-##
-## Parses & constructs URLs.
-
-{.deprecated.}
-
-import strutils
-
-type
-  Url* = tuple[      ## represents a *Uniform Resource Locator* (URL)
-                     ## any optional component is "" if it does not exist
-    scheme, username, password,
-    hostname, port, path, query, anchor: string]
-
-proc parseUrl*(url: string): Url {.deprecated.} =
-  var i = 0
-
-  var scheme, username, password: string = ""
-  var hostname, port, path, query, anchor: string = ""
-
-  var temp = ""
-
-  if url[i] != '/': # url isn't a relative path
-    while true:
-      # Scheme
-      if url[i] == ':':
-        if url[i+1] == '/' and url[i+2] == '/':
-          scheme = temp
-          temp.setLen(0)
-          inc(i, 3) # Skip the //
-      # Authority(username, password)
-      if url[i] == '@':
-        username = temp
-        let colon = username.find(':')
-        if colon >= 0:
-          password = username.substr(colon+1)
-          username = username.substr(0, colon-1)
-        temp.setLen(0)
-        inc(i) #Skip the @
-      # hostname(subdomain, domain, port)
-      if url[i] == '/' or url[i] == '\0':
-        hostname = temp
-        let colon = hostname.find(':')
-        if colon >= 0:
-          port = hostname.substr(colon+1)
-          hostname = hostname.substr(0, colon-1)
-
-        temp.setLen(0)
-        break
-
-      temp.add(url[i])
-      inc(i)
-
-  if url[i] == '/': inc(i) # Skip the '/'
-  # Path
-  while true:
-    if url[i] == '?':
-      path = temp
-      temp.setLen(0)
-    if url[i] == '#':
-      if temp[0] == '?':
-        query = temp
-      else:
-        path = temp
-      temp.setLen(0)
-
-    if url[i] == '\0':
-      if temp[0] == '?':
-        query = temp
-      elif temp[0] == '#':
-        anchor = temp
-      else:
-        path = temp
-      break
-
-    temp.add(url[i])
-    inc(i)
-
-  return (scheme, username, password, hostname, port, path, query, anchor)
-
-proc `$`*(u: Url): string {.deprecated.} =
-  ## turns the URL `u` into its string representation.
-  result = ""
-  if u.scheme.len > 0:
-    result.add(u.scheme)
-    result.add("://")
-  if u.username.len > 0:
-    result.add(u.username)
-    if u.password.len > 0:
-      result.add(":")
-      result.add(u.password)
-    result.add("@")
-  result.add(u.hostname)
-  if u.port.len > 0:
-    result.add(":")
-    result.add(u.port)
-  if u.path.len > 0:
-    result.add("/")
-    result.add(u.path)
-  result.add(u.query)
-  result.add(u.anchor)
-
diff --git a/lib/deprecated/pure/rawsockets.nim b/lib/deprecated/pure/rawsockets.nim
deleted file mode 100644
index 876334f9e..000000000
--- a/lib/deprecated/pure/rawsockets.nim
+++ /dev/null
@@ -1,14 +0,0 @@
-import nativesockets
-export nativesockets
-
-{.warning: "rawsockets module is deprecated, use nativesockets instead".}
-
-template newRawSocket*(domain, sockType, protocol: cint): untyped =
-  {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
-  newNativeSocket(domain, sockType, protocol)
-
-template newRawSocket*(domain: Domain = AF_INET,
-                       sockType: SockType = SOCK_STREAM,
-                       protocol: Protocol = IPPROTO_TCP): untyped =
-  {.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
-  newNativeSocket(domain, sockType, protocol)
diff --git a/lib/pure/securehash.nim b/lib/deprecated/pure/securehash.nim
index c6cde599a..c6cde599a 100644
--- a/lib/pure/securehash.nim
+++ b/lib/deprecated/pure/securehash.nim
diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim
index bffce2803..253355707 100644
--- a/lib/experimental/diff.nim
+++ b/lib/experimental/diff.nim
@@ -252,8 +252,11 @@ proc createDiffs(dataA, dataB: DiffData): seq[Item] =
 
 proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
   ## Find the difference in 2 arrays of integers.
+  ##
   ## ``arrayA`` A-version of the numbers (usualy the old one)
+  ##
   ## ``arrayB`` B-version of the numbers (usualy the new one)
+  ##
   ## Returns a array of Items that describe the differences.
 
   # The A-Version of the data (original data) to be compared.
@@ -273,15 +276,16 @@ proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
 
 proc diffText*(textA, textB: string): seq[Item] =
   ## Find the difference in 2 text documents, comparing by textlines.
+  ##
   ## The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
   ## each line is converted into a (hash) number. This hash-value is computed by storing all
   ## textlines into a common hashtable so i can find dublicates in there, and generating a
   ## new number each time a new textline is inserted.
-  ## ``TextA`` A-version of the text (usualy the old one)
-  ## ``TextB`` B-version of the text (usualy the new one)
-  ## ``trimSpace`` When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.
-  ## ``ignoreSpace`` When set to true, all whitespace characters are converted to a single space character before the comparation is done.
-  ## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
+  ##
+  ## ``textA`` A-version of the text (usually the old one)
+  ##
+  ## ``textB`` B-version of the text (usually the new one)
+  ##
   ## Returns a seq of Items that describe the differences.
 
   # prepare the input-text and convert to comparable numbers.
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
index 72d7aa800..b7af5128a 100644
--- a/lib/impure/db_odbc.nim
+++ b/lib/impure/db_odbc.nim
@@ -131,7 +131,7 @@ proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
               511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
   except:
     discard
-  return (res.int, $sqlState, $nativeErr, $errMsg)
+  return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
 
 proc dbError*(db: var DbConn) {.
           tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index c7e373098..3910992bb 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -166,10 +166,12 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
   var stmt = setupQuery(db, query, args)
   var L = (column_count(stmt))
   var result = newRow(L)
-  while step(stmt) == SQLITE_ROW:
-    setRow(stmt, result, L)
-    yield result
-  if finalize(stmt) != SQLITE_OK: dbError(db)
+  try:
+    while step(stmt) == SQLITE_ROW:
+      setRow(stmt, result, L)
+      yield result
+  finally:
+    if finalize(stmt) != SQLITE_OK: dbError(db)
 
 iterator instantRows*(db: DbConn, query: SqlQuery,
                       args: varargs[string, `$`]): InstantRow
@@ -177,9 +179,11 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
   ## same as fastRows but returns a handle that can be used to get column text
   ## on demand using []. Returned handle is valid only within the iterator body.
   var stmt = setupQuery(db, query, args)
-  while step(stmt) == SQLITE_ROW:
-    yield stmt
-  if finalize(stmt) != SQLITE_OK: dbError(db)
+  try:
+    while step(stmt) == SQLITE_ROW:
+      yield stmt
+  finally:
+    if finalize(stmt) != SQLITE_OK: dbError(db)
 
 proc toTypeKind(t: var DbType; x: int32) =
   case x
@@ -210,9 +214,11 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
   ## on demand using []. Returned handle is valid only within the iterator body.
   var stmt = setupQuery(db, query, args)
   setColumns(columns, stmt)
-  while step(stmt) == SQLITE_ROW:
-    yield stmt
-  if finalize(stmt) != SQLITE_OK: dbError(db)
+  try:
+    while step(stmt) == SQLITE_ROW:
+      yield stmt
+  finally:
+    if finalize(stmt) != SQLITE_OK: dbError(db)
 
 proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
   ## returns text for given column of the row
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 94dd89db5..5c5125ba1 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -540,7 +540,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
       raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
 
 proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
-  ## Like ```find(...)`` <#proc-find>`_, but anchored to the start of the
+  ## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the
   ## string.
   ##
   runnableExamples:
@@ -550,11 +550,11 @@ proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[R
   return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
 
 iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
-  ## Works the same as ```find(...)`` <#proc-find>`_, but finds every
+  ## Works the same as ` ``find(...)`` <#proc-find>`_, but finds every
   ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
   ## ``"22", "22", "22"``.
   ##
-  ## Arguments are the same as ```find(...)`` <#proc-find>`_
+  ## Arguments are the same as ` ``find(...)`` <#proc-find>`_
   ##
   ## Variants:
   ##
@@ -633,7 +633,7 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
   ## Splits the string with the given regex. This works according to the
   ## rules that Perl and Javascript use.
   ##
-  ## ``start`` behaves the same as in ```find(...)`` <#proc-find>`_.
+  ## ``start`` behaves the same as in ` ``find(...)`` <#proc-find>`_.
   ##
   runnableExamples:
     # -  If the match is zero-width, then the string is still split:
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index dc4ee326f..42be4a3c2 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -561,31 +561,6 @@ proc escapeRe*(s: string): string =
       result.add("\\x")
       result.add(toHex(ord(c), 2))
 
-const ## common regular expressions
-  reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
-    ## describes an identifier
-  reNatural* {.deprecated.} = r"\b\d+\b"
-    ## describes a natural number
-  reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
-    ## describes an integer
-  reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
-    ## describes a hexadecimal number
-  reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
-    ## describes a binary number (example: 0b11101)
-  reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
-    ## describes an octal number (example: 0o777)
-  reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
-    ## describes a floating point number
-  reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
-                            r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
-                            r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
-                            r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
-                            r"info|mobi|name|aero|jobs|museum)\b"
-    ## describes a common email address
-  reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
-                          r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
-    ## describes an URL
-
 when isMainModule:
   doAssert match("(a b c)", rex"\( .* \)")
   doAssert match("WHiLe", re("while", {reIgnoreCase}))
@@ -595,7 +570,7 @@ when isMainModule:
   doAssert "ABC".match(rex"\d+ | \w+")
 
   {.push warnings:off.}
-  doAssert matchLen("key", re(reIdentifier)) == 3
+  doAssert matchLen("key", re"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b") == 3
   {.pop.}
 
   var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"
diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim
deleted file mode 100644
index 0bcb3f217..000000000
--- a/lib/impure/ssl.nim
+++ /dev/null
@@ -1,99 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module provides an easy to use sockets-style
-## nim interface to the OpenSSL library.
-##
-## **Warning:** This module is deprecated, use the SSL procedures defined in
-## the ``net`` module instead.
-
-{.deprecated.}
-
-import openssl, strutils, os
-
-type
-  SecureSocket* = object
-    ssl: SslPtr
-    bio: BIO
-
-proc connect*(sock: var SecureSocket, address: string,
-    port: int): int =
-  ## Connects to the specified `address` on the specified `port`.
-  ## Returns the result of the certificate validation.
-  SslLoadErrorStrings()
-  ERR_load_BIO_strings()
-
-  if SSL_library_init() != 1:
-    raiseOSError(osLastError())
-
-  var ctx = SSL_CTX_new(SSLv23_client_method())
-  if ctx == nil:
-    ERR_print_errors_fp(stderr)
-    raiseOSError(osLastError())
-
-  #if SSL_CTX_load_verify_locations(ctx,
-  #   "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
-  #  echo("Failed load verify locations")
-  #  ERR_print_errors_fp(stderr)
-
-  sock.bio = BIO_new_ssl_connect(ctx)
-  if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
-    raiseOSError(osLastError())
-
-  if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
-    raiseOSError(osLastError())
-
-  if BIO_do_connect(sock.bio) <= 0:
-    ERR_print_errors_fp(stderr)
-    raiseOSError(osLastError())
-
-  result = SSL_get_verify_result(sock.ssl)
-
-proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
-  ## Acts in a similar fashion to the `recvLine` in the sockets module.
-  ## Returns false when no data is available to be read.
-  ## `Line` must be initialized and not nil!
-  setLen(line.string, 0)
-  while true:
-    var c: array[0..0, char]
-    var n = BIO_read(sock.bio, addr c, c.len.cint)
-    if n <= 0: return false
-    if c[0] == '\r':
-      n = BIO_read(sock.bio, addr c, c.len.cint)
-      if n > 0 and c[0] == '\L':
-        return true
-      elif n <= 0:
-        return false
-    elif c[0] == '\L': return true
-    add(line.string, c[0])
-
-
-proc send*(sock: SecureSocket, data: string) =
-  ## Writes `data` to the socket.
-  if BIO_write(sock.bio, data, data.len.cint) <= 0:
-    raiseOSError(osLastError())
-
-proc close*(sock: SecureSocket) =
-  ## Closes the socket
-  if BIO_free(sock.bio) <= 0:
-    ERR_print_errors_fp(stderr)
-    raiseOSError(osLastError())
-
-when not defined(testing) and isMainModule:
-  var s: SecureSocket
-  echo connect(s, "smtp.gmail.com", 465)
-
-  #var buffer: array[0..255, char]
-  #echo BIO_read(bio, buffer, buffer.len)
-  var buffer: string = ""
-
-  echo s.recvLine(buffer)
-  echo buffer
-  echo buffer.len
-
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index cf219df3d..668dee822 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -416,12 +416,12 @@ type
   BoundingRect* {.importc.} = ref object
     top*, bottom*, left*, right*, x*, y*, width*, height*: float
 
-  PerformanceMemory* {.importc.} = ref object 
+  PerformanceMemory* {.importc.} = ref object
     jsHeapSizeLimit*: float
     totalJSHeapSize*: float
     usedJSHeapSize*: float
 
-  PerformanceTiming* {.importc.} = ref object 
+  PerformanceTiming* {.importc.} = ref object
     connectStart*: float
     domComplete*: float
     domContentLoadedEventEnd*: float
@@ -459,7 +459,6 @@ proc dispatchEvent*(et: EventTarget, ev: Event)
 proc alert*(w: Window, msg: cstring)
 proc back*(w: Window)
 proc blur*(w: Window)
-proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
 proc clearInterval*(w: Window, interval: ref TInterval)
 proc clearTimeout*(w: Window, timeout: ref TTimeOut)
 proc close*(w: Window)
@@ -478,7 +477,6 @@ proc open*(w: Window, uri, windowname: cstring,
            properties: cstring = nil): Window
 proc print*(w: Window)
 proc prompt*(w: Window, text, default: cstring): cstring
-proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
 proc resizeBy*(w: Window, x, y: int)
 proc resizeTo*(w: Window, x, y: int)
 proc routeEvent*(w: Window, event: Event)
@@ -513,7 +511,6 @@ proc setAttribute*(n: Node, name, value: cstring)
 proc setAttributeNode*(n: Node, attr: Node)
 
 # Document "methods"
-proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
 proc createAttribute*(d: Document, identifier: cstring): Node
 proc createElement*(d: Document, identifier: cstring): Element
 proc createTextNode*(d: Document, identifier: cstring): Node
@@ -524,7 +521,6 @@ proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
 proc getSelection*(d: Document): cstring
 proc handleEvent*(d: Document, event: Event)
 proc open*(d: Document)
-proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
 proc routeEvent*(d: Document, event: Event)
 proc write*(d: Document, text: cstring)
 proc writeln*(d: Document, text: cstring)
@@ -605,25 +601,3 @@ proc parseInt*(s: cstring): int {.importc, nodecl.}
 proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
 
 proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
-
-type
-  TEventHandlers* {.deprecated.} = EventTargetObj
-  TWindow* {.deprecated.} = WindowObj
-  TFrame* {.deprecated.} = FrameObj
-  TNode* {.deprecated.} = NodeObj
-  TDocument* {.deprecated.} = DocumentObj
-  TElement* {.deprecated.} = ElementObj
-  TLink* {.deprecated.} = LinkObj
-  TEmbed* {.deprecated.} = EmbedObj
-  TAnchor* {.deprecated.} = AnchorObj
-  TOption* {.deprecated.} = OptionObj
-  TForm* {.deprecated.} = FormObj
-  TImage* {.deprecated.} = ImageObj
-  TNodeType* {.deprecated.} = NodeType
-  TEvent* {.deprecated.} = EventObj
-  TLocation* {.deprecated.} = LocationObj
-  THistory* {.deprecated.} = HistoryObj
-  TNavigator* {.deprecated.} = NavigatorObj
-  TStyle* {.deprecated.} = StyleObj
-  TScreen* {.deprecated.} = ScreenObj
-  TApplet* {.importc, deprecated.} = object of RootObj
diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim
index bf64b0794..2e2bd2402 100644
--- a/lib/js/jscore.nim
+++ b/lib/js/jscore.nim
@@ -73,7 +73,7 @@ proc parse*(d: DateLib, s: cstring): int {.importcpp.}
 proc newDate*(): DateTime {.
   importcpp: "new Date()".}
 
-proc newDate*(date: int|string): DateTime {.
+proc newDate*(date: int|int64|string): DateTime {.
   importcpp: "new Date(#)".}
 
 proc newDate*(year, month, day, hours, minutes,
@@ -90,6 +90,16 @@ proc getSeconds*(d: DateTime): int {.importcpp.}
 proc getYear*(d: DateTime): int {.importcpp.}
 proc getTime*(d: DateTime): int {.importcpp.}
 proc toString*(d: DateTime): cstring {.importcpp.}
+proc getUTCDate*(d: DateTime): int {.importcpp.}
+proc getUTCFullYear*(d: DateTime): int {.importcpp.}
+proc getUTCHours*(d: DateTime): int {.importcpp.}
+proc getUTCMilliseconds*(d: DateTime): int {.importcpp.}
+proc getUTCMinutes*(d: DateTime): int {.importcpp.}
+proc getUTCMonth*(d: DateTime): int {.importcpp.}
+proc getUTCSeconds*(d: DateTime): int {.importcpp.}
+proc getUTCDay*(d: DateTime): int {.importcpp.}
+proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
+proc setFullYear*(d: DateTime, year: int) {.importcpp.}
 
 #JSON library
 proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index e1c59803d..7f0939b08 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -104,6 +104,12 @@ var
   jsFilename* {.importc: "__filename", nodecl.}: cstring
     ## JavaScript's __filename pseudo-variable
 
+proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".}
+  ## check if a value is exactly null
+
+proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".}
+  ## check if a value is exactly undefined
+
 # Exceptions
 type
   JsError* {.importc: "Error".} = object of JsRoot
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 2cb632787..ba4273726 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -166,13 +166,13 @@ __clang__
 #  define N_STDCALL(rettype, name) rettype __stdcall name
 #  define N_SYSCALL(rettype, name) rettype __syscall name
 #  define N_FASTCALL(rettype, name) rettype __fastcall name
-#  define N_SAFECALL(rettype, name) rettype __safecall name
+#  define N_SAFECALL(rettype, name) rettype __stdcall name
 /* function pointers with calling convention: */
 #  define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
 #  define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
 #  define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
 #  define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
-#  define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
+#  define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
 
 #  ifdef __cplusplus
 #    define N_LIB_EXPORT  extern "C" __declspec(dllexport)
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 161509afe..551df9919 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -560,9 +560,9 @@ proc match(p: RstParser, start: int, expr: string): bool =
       result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
       if result:
         case p.tok[j].symbol[0]
-        of 'a'..'z', 'A'..'Z': result = len(p.tok[j].symbol) == 1
+        of 'a'..'z', 'A'..'Z', '#': result = len(p.tok[j].symbol) == 1
         of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
-        else: discard
+        else: result = false
     else:
       var c = expr[i]
       var length = 0
@@ -780,6 +780,31 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
   add(result, nil)
   add(result, lb)
 
+proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
+  result = true
+  var desc, link = ""
+  var i = p.idx
+
+  template parse(endToken, dest) =
+    inc i # skip begin token
+    while true:
+      if p.tok[i].kind in {tkEof, tkIndent}: return false
+      if p.tok[i].symbol == endToken: break
+      dest.add p.tok[i].symbol
+      inc i
+    inc i # skip end token
+
+  parse("]", desc)
+  if p.tok[i].symbol != "(": return false
+  parse(")", link)
+  let child = newRstNode(rnHyperlink)
+  child.add desc
+  child.add link
+  # only commit if we detected no syntax error:
+  father.add child
+  p.idx = i
+  result = true
+
 proc parseInline(p: var RstParser, father: PRstNode) =
   case p.tok[p.idx].kind
   of tkPunct:
@@ -811,6 +836,9 @@ proc parseInline(p: var RstParser, father: PRstNode) =
       var n = newRstNode(rnSubstitutionReferences)
       parseUntil(p, n, "|", false)
       add(father, n)
+    elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "[" and
+        parseMarkdownLink(p, father):
+      discard "parseMarkdownLink already processed it"
     else:
       if roSupportSmilies in p.s.options:
         let n = parseSmiley(p)
@@ -1037,15 +1065,32 @@ proc isOptionList(p: RstParser): bool =
   result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
            match(p, p.idx, "/w") or match(p, p.idx, "//w")
 
+proc isMarkdownHeadlinePattern(s: string): bool =
+  if s.len >= 1 and s.len <= 6:
+    for c in s:
+      if c != '#': return false
+    result = true
+
+proc isMarkdownHeadline(p: RstParser): bool =
+  if roSupportMarkdown in p.s.options:
+    if isMarkdownHeadlinePattern(p.tok[p.idx].symbol) and p.tok[p.idx+1].kind == tkWhite:
+      if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
+        result = true
+
 proc whichSection(p: RstParser): RstNodeKind =
   case p.tok[p.idx].kind
   of tkAdornment:
     if match(p, p.idx + 1, "ii"): result = rnTransition
     elif match(p, p.idx + 1, " a"): result = rnTable
     elif match(p, p.idx + 1, "i"): result = rnOverline
-    else: result = rnLeaf
+    elif isMarkdownHeadline(p):
+      result = rnHeadline
+    else:
+      result = rnLeaf
   of tkPunct:
-    if match(p, tokenAfterNewline(p), "ai"):
+    if isMarkdownHeadline(p):
+      result = rnHeadline
+    elif match(p, tokenAfterNewline(p), "ai"):
       result = rnHeadline
     elif p.tok[p.idx].symbol == "::":
       result = rnLiteralBlock
@@ -1060,7 +1105,7 @@ proc whichSection(p: RstParser): RstNodeKind =
     elif match(p, p.idx, ":w:") and predNL(p):
       # (p.tok[p.idx].symbol == ":")
       result = rnFieldList
-    elif match(p, p.idx, "(e) "):
+    elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
       result = rnEnumList
     elif match(p, p.idx, "+a+"):
       result = rnGridTable
@@ -1130,12 +1175,18 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
 
 proc parseHeadline(p: var RstParser): PRstNode =
   result = newRstNode(rnHeadline)
-  parseUntilNewline(p, result)
-  assert(p.tok[p.idx].kind == tkIndent)
-  assert(p.tok[p.idx + 1].kind == tkAdornment)
-  var c = p.tok[p.idx + 1].symbol[0]
-  inc(p.idx, 2)
-  result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
+  if isMarkdownHeadline(p):
+    result.level = p.tok[p.idx].symbol.len
+    assert(p.tok[p.idx+1].kind == tkWhite)
+    inc p.idx, 2
+    parseUntilNewline(p, result)
+  else:
+    parseUntilNewline(p, result)
+    assert(p.tok[p.idx].kind == tkIndent)
+    assert(p.tok[p.idx + 1].kind == tkAdornment)
+    var c = p.tok[p.idx + 1].symbol[0]
+    inc(p.idx, 2)
+    result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
 
 type
   IntSeq = seq[int]
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index 4a77b4f34..fee824b09 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -89,6 +89,9 @@ proc lastSon*(n: PRstNode): PRstNode =
 proc add*(father, son: PRstNode) =
   add(father.sons, son)
 
+proc add*(father: PRstNode; s: string) =
+  add(father.sons, newRstNode(rnLeaf, s))
+
 proc addIfNotNil*(father, son: PRstNode) =
   if son != nil: add(father, son)
 
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 232da5c93..766ce6ffd 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -1134,11 +1134,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   of rnTripleEmphasis:
     renderAux(d, n, "<strong><em>$1</em></strong>",
                     "\\textbf{emph{$1}}", result)
-  of rnInterpretedText:
-    renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
   of rnIdx:
     renderIndexTerm(d, n, result)
-  of rnInlineLiteral:
+  of rnInlineLiteral, rnInterpretedText:
     renderAux(d, n,
       "<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
       "\\texttt{$1}", result)
diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim
index c23005b1e..dfbfe7f64 100644
--- a/lib/posix/posix_linux_amd64_consts.nim
+++ b/lib/posix/posix_linux_amd64_consts.nim
@@ -295,6 +295,7 @@ const IF_NAMESIZE* = cint(16)
 const IPPROTO_IP* = cint(0)
 const IPPROTO_IPV6* = cint(41)
 const IPPROTO_ICMP* = cint(1)
+const IPPROTO_ICMPV6* = cint(58)
 const IPPROTO_RAW* = cint(255)
 const IPPROTO_TCP* = cint(6)
 const IPPROTO_UDP* = cint(17)
diff --git a/lib/posix/posix_nintendoswitch_consts.nim b/lib/posix/posix_nintendoswitch_consts.nim
index f0c0dd717..1e782d92e 100644
--- a/lib/posix/posix_nintendoswitch_consts.nim
+++ b/lib/posix/posix_nintendoswitch_consts.nim
@@ -237,6 +237,7 @@ const IF_NAMESIZE* = cint(16)
 const IPPROTO_IP* = cint(0)
 const IPPROTO_IPV6* = cint(41)
 const IPPROTO_ICMP* = cint(1)
+const IPPROTO_ICMPV6* = cint(58)
 const IPPROTO_RAW* = cint(255)
 const IPPROTO_TCP* = cint(6)
 const IPPROTO_UDP* = cint(17)
diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim
index 2b4b70941..cd5199078 100644
--- a/lib/posix/posix_other_consts.nim
+++ b/lib/posix/posix_other_consts.nim
@@ -302,6 +302,7 @@ var IF_NAMESIZE* {.importc: "IF_NAMESIZE", header: "<net/if.h>".}: cint
 var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
 var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "<netinet/in.h>".}: cint
 var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "<netinet/in.h>".}: cint
+var IPPROTO_ICMPV6* {.importc: "IPPROTO_ICMPV6", header: "<netinet/in.h>".}: cint
 var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "<netinet/in.h>".}: cint
 var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "<netinet/in.h>".}: cint
 var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "<netinet/in.h>".}: cint
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index d27c2fb9c..e3fc75597 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -143,11 +143,14 @@ proc parseUppercaseMethod(name: string): HttpMethod =
     of "TRACE": HttpTrace
     else: raise newException(ValueError, "Invalid HTTP method " & name)
 
-proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
-                    client: AsyncSocket,
-                    address: string, lineFut: FutureVar[string],
-                    callback: proc (request: Request):
-                      Future[void] {.closure, gcsafe.}) {.async.} =
+proc processRequest(
+  server: AsyncHttpServer,
+  req: FutureVar[Request],
+  client: AsyncSocket,
+  address: string,
+  lineFut: FutureVar[string],
+  callback: proc (request: Request): Future[void] {.closure, gcsafe.},
+): Future[bool] {.async.} =
 
   # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
   template request(): Request =
@@ -171,12 +174,12 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
 
     if lineFut.mget == "":
       client.close()
-      return
+      return false
 
     if lineFut.mget.len > maxLine:
       await request.respondError(Http413)
       client.close()
-      return
+      return false
     if lineFut.mget != "\c\L":
       break
 
@@ -189,22 +192,22 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
         request.reqMethod = parseUppercaseMethod(linePart)
       except ValueError:
         asyncCheck request.respondError(Http400)
-        return
+        return true # Retry processing of request
     of 1:
       try:
         parseUri(linePart, request.url)
       except ValueError:
         asyncCheck request.respondError(Http400)
-        return
+        return true
     of 2:
       try:
         request.protocol = parseProtocol(linePart)
       except ValueError:
         asyncCheck request.respondError(Http400)
-        return
+        return true
     else:
       await request.respondError(Http400)
-      return
+      return true
     inc i
 
   # Headers
@@ -215,10 +218,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     await client.recvLineInto(lineFut, maxLength=maxLine)
 
     if lineFut.mget == "":
-      client.close(); return
+      client.close(); return false
     if lineFut.mget.len > maxLine:
       await request.respondError(Http413)
-      client.close(); return
+      client.close(); return false
     if lineFut.mget == "\c\L": break
     let (key, value) = parseHeader(lineFut.mget)
     request.headers[key] = value
@@ -226,7 +229,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     if request.headers.len > headerLimit:
       await client.sendStatus("400 Bad Request")
       request.client.close()
-      return
+      return false
 
   if request.reqMethod == HttpPost:
     # Check for Expect header
@@ -242,24 +245,24 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     var contentLength = 0
     if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
       await request.respond(Http400, "Bad Request. Invalid Content-Length.")
-      return
+      return true
     else:
       if contentLength > server.maxBody:
         await request.respondError(Http413)
-        return
+        return false
       request.body = await client.recv(contentLength)
       if request.body.len != contentLength:
         await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
-        return
+        return true
   elif request.reqMethod == HttpPost:
     await request.respond(Http411, "Content-Length required.")
-    return
+    return true
 
   # Call the user's callback.
   await callback(request)
 
   if "upgrade" in request.headers.getOrDefault("connection"):
-    return
+    return false
 
   # Persistent connections
   if (request.protocol == HttpVer11 and
@@ -273,7 +276,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
     discard
   else:
     request.client.close()
-    return
+    return false
 
 proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
                    callback: proc (request: Request):
@@ -285,7 +288,10 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
   lineFut.mget() = newStringOfCap(80)
 
   while not client.isClosed:
-    await processRequest(server, request, client, address, lineFut, callback)
+    let retry = await processRequest(
+      server, request, client, address, lineFut, callback
+    )
+    if not retry: break
 
 proc serve*(server: AsyncHttpServer, port: Port,
             callback: proc (request: Request): Future[void] {.closure,gcsafe.},
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index bfb8a1666..427f93926 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -9,37 +9,49 @@
 
 ## This module implements a base64 encoder and decoder.
 ##
+## Base64 is an encoding and decoding technique used to convert binary
+## data to an ASCII string format.
+## Each Base64 digit represents exactly 6 bits of data. Three 8-bit
+## bytes (i.e., a total of 24 bits) can therefore be represented by
+## four 6-bit Base64 digits.
+##
+##
+## Basic usage
+## ===========
+##
 ## Encoding data
 ## -------------
 ##
-## In order to encode some text simply call the ``encode`` procedure:
-##
-##   .. code-block::nim
-##      import base64
-##      let encoded = encode("Hello World")
-##      echo(encoded) # SGVsbG8gV29ybGQ=
+## .. code-block::nim
+##    import base64
+##    let encoded = encode("Hello World")
+##    assert encoded == "SGVsbG8gV29ybGQ="
 ##
 ## Apart from strings you can also encode lists of integers or characters:
 ##
-##   .. code-block::nim
-##      import base64
-##      let encodedInts = encode([1,2,3])
-##      echo(encodedInts) # AQID
-##      let encodedChars = encode(['h','e','y'])
-##      echo(encodedChars) # aGV5
+## .. code-block::nim
+##    import base64
+##    let encodedInts = encode([1,2,3])
+##    assert encodedInts == "AQID"
+##    let encodedChars = encode(['h','e','y'])
+##    assert encodedChars == "aGV5"
 ##
-## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
-## can be passed as parameters.
 ##
 ## Decoding data
 ## -------------
 ##
-## To decode a base64 encoded data string simply call the ``decode``
-## procedure:
+## .. code-block::nim
+##    import base64
+##    let decoded = decode("SGVsbG8gV29ybGQ=")
+##    assert decoded == "Hello World"
 ##
-##   .. code-block::nim
-##      import base64
-##      echo(decode("SGVsbG8gV29ybGQ=")) # Hello World
+##
+## See also
+## ========
+##
+## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
+## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
+## * `sha1 module<sha1.html>`_ implements a sha1 encoder and decoder
 
 const
   cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
     discard
 
 proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
+  ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
+  ## ``newline`` is added.
   ##
   ## This procedure encodes an openarray (array or sequence) of either integers
   ## or characters.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,string,int,string>`_ for encoding a string
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encode(['n', 'i', 'm']) == "bmlt"
+    assert encode(@['n', 'i', 'm']) == "bmlt"
+    assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
   encodeInternal(s, lineLen, newLine)
 
 proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
-  ## encodes `s` into base64 representation. After `lineLen` characters, a
-  ## `newline` is added.
+  ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
+  ## ``newline`` is added.
   ##
   ## This procedure encodes a string.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
+  ## * `decode proc<#decode,string>`_ for decoding a string
+  runnableExamples:
+    assert encode("Hello World") == "SGVsbG8gV29ybGQ="
+    assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
   encodeInternal(s, lineLen, newLine)
 
 proc decodeByte(b: char): int {.inline.} =
@@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
   else: result = 63
 
 proc decode*(s: string): string =
-  ## decodes a string in base64 representation back into its original form.
-  ## Whitespace is skipped.
+  ## Decodes string ``s`` in base64 representation back into its original form.
+  ## The initial whitespace is skipped.
+  ##
+  ## **See also:**
+  ## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
+  ## * `encode proc<#encode,string,int,string>`_ for encoding a string
+  runnableExamples:
+    assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
+    assert decode("  SGVsbG8gV29ybGQ=") == "Hello World"
   const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
   var total = ((len(s) + 3) div 4) * 3
   # total is an upper bound, as we will skip arbitrary whitespace:
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 869abc9cc..ec3562c35 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -337,11 +337,6 @@ proc setStackTraceStdout*() =
   ## Makes Nim output stacktraces to stdout, instead of server log.
   errorMessageWriter = writeErrorMessage
 
-proc setStackTraceNewLine*() {.deprecated.} =
-  ## Makes Nim output stacktraces to stdout, instead of server log.
-  ## Depracated alias for setStackTraceStdout.
-  setStackTraceStdout()
-
 proc setCookie*(name, value: string) =
   ## Sets a cookie.
   write(stdout, "Set-Cookie: ", name, "=", value, "\n")
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 32e0299ba..dd91fdb12 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -202,12 +202,6 @@ proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
   ## If `key` is not in `t`, the ``KeyError`` exception is raised.
   get(c, key)
 
-proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} =
-  ## retrieves the value at ``c[key]``. The value can be modified.
-  ## If `key` is not in `t`, the ``KeyError`` exception is raised.
-  ## Use ```[]``` instead.
-  get(c, key)
-
 iterator leaves[T](n: Node[T]): Node[T] =
   if n != nil:
     # XXX actually we could compute the necessary stack size in advance:
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index e8342e208..cb05e5112 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -20,41 +20,59 @@
 ## access, unless your program logic guarantees it indirectly.
 ##
 ## .. code-block:: Nim
-##   proc foo(a, b: Positive) =  # assume random positive values for `a` and `b`
-##     var deq = initDeque[int]()  # initializes the object
-##     for i in 1 ..< a: deq.addLast i  # populates the deque
+##   import deques
 ##
-##     if b < deq.len:  # checking before indexed access
-##       echo "The element at index position ", b, " is ", deq[b]
+##   var a = initDeque[int]()
 ##
-##     # The following two lines don't need any checking on access due to the
-##     # logic of the program, but that would not be the case if `a` could be 0.
-##     assert deq.peekFirst == 1
-##     assert deq.peekLast == a
+##   doAssertRaises(IndexError, echo a[0])
 ##
-##     while deq.len > 0:  # checking if the deque is empty
-##       echo deq.popLast()
+##   for i in 1 .. 5:
+##     a.addLast(10*i)
+##   assert $a == "[10, 20, 30, 40, 50]"
 ##
-## Note: For inter thread communication use
-## a `Channel <channels.html>`_ instead.
+##   assert a.peekFirst == 10
+##   assert a.peekLast == 50
+##   assert len(a) == 5
+##
+##   assert a.popFirst == 10
+##   assert a.popLast == 50
+##   assert len(a) == 3
+##
+##   a.addFirst(11)
+##   a.addFirst(22)
+##   a.addFirst(33)
+##   assert $a == "[33, 22, 11, 20, 30, 40]"
+##
+##   a.shrink(fromFirst = 1, fromLast = 2)
+##   assert $a == "[22, 11, 20]"
+##
+##
+## **See also:**
+## * `lists module <lists.html>`_ for singly and doubly linked lists and rings
+## * `channels module <channels.html>`_ for inter-thread communication
+
 
 import math, typetraits
 
 type
   Deque*[T] = object
     ## A double-ended queue backed with a ringed seq buffer.
+    ##
+    ## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
     data: seq[T]
     head, tail, count, mask: int
 
 proc initDeque*[T](initialSize: int = 4): Deque[T] =
-  ## Create a new deque.
-  ## Optionally, the initial capacity can be reserved via `initialSize` as a
-  ## performance optimization. The length of a newly created deque will still
-  ## be 0.
+  ## Create a new empty deque.
   ##
-  ## `initialSize` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module.
+  ## Optionally, the initial capacity can be reserved via `initialSize`
+  ## as a performance optimization.
+  ## The length of a newly created deque will still be 0.
+  ##
+  ## ``initialSize`` must be a power of two (default: 4).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_.
   assert isPowerOfTwo(initialSize)
   result.mask = initialSize-1
   newSeq(result.data, initialSize)
@@ -75,33 +93,128 @@ template xBoundsCheck(deq, i) =
     if unlikely(i >= deq.count):  # x < deq.low is taken care by the Natural parameter
       raise newException(IndexError,
                          "Out of bounds: " & $i & " > " & $(deq.count - 1))
+    if unlikely(i < 0):  # when used with BackwardsIndex
+      raise newException(IndexError,
+                         "Out of bounds: " & $i & " < 0")
 
 proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
-  ## Access the i-th element of `deq` by order from first to last.
-  ## deq[0] is the first, deq[^1] is the last.
+  ## Access the i-th element of `deq`.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert a[0] == 10
+    assert a[3] == 40
+    doAssertRaises(IndexError, echo a[8])
+
   xBoundsCheck(deq, i)
   return deq.data[(deq.head + i) and deq.mask]
 
 proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
-  ## Access the i-th element of `deq` and returns a mutable
+  ## Access the i-th element of `deq` and return a mutable
   ## reference to it.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert a[0] == 10
+    assert a[3] == 40
+    doAssertRaises(IndexError, echo a[8])
+
   xBoundsCheck(deq, i)
   return deq.data[(deq.head + i) and deq.mask]
 
-proc `[]=`* [T] (deq: var Deque[T], i: Natural, val : T) {.inline.} =
+proc `[]=`*[T](deq: var Deque[T], i: Natural, val : T) {.inline.} =
   ## Change the i-th element of `deq`.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    a[0] = 99
+    a[3] = 66
+    assert $a == "[99, 20, 30, 66, 50]"
+
   xBoundsCheck(deq, i)
   deq.data[(deq.head + i) and deq.mask] = val
 
+proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
+  ## Access the backwards indexed i-th element.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert a[^1] == 50
+    assert a[^4] == 20
+    doAssertRaises(IndexError, echo a[^9])
+
+  xBoundsCheck(deq, deq.len - int(i))
+  return deq[deq.len - int(i)]
+
+proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} =
+  ## Access the backwards indexed i-th element.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert a[^1] == 50
+    assert a[^4] == 20
+    doAssertRaises(IndexError, echo a[^9])
+
+  xBoundsCheck(deq, deq.len - int(i))
+  return deq[deq.len - int(i)]
+
+proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} =
+  ## Change the backwards indexed i-th element.
+  ##
+  ## `deq[^1]` is the last element.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    a[^1] = 99
+    a[^3] = 77
+    assert $a == "[10, 20, 77, 40, 99]"
+
+  xBoundsCheck(deq, deq.len - int(i))
+  deq[deq.len - int(i)] = x
+
 iterator items*[T](deq: Deque[T]): T =
   ## Yield every element of `deq`.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = initDeque[int]()
+  ##   for i in 1 .. 3:
+  ##     a.addLast(10*i)
+  ##
+  ##   for x in a:  # the same as: for x in items(a):
+  ##     echo x
+  ##
+  ##   # 10
+  ##   # 20
+  ##   # 30
+  ##
   var i = deq.head
   for c in 0 ..< deq.count:
     yield deq.data[i]
     i = (i + 1) and deq.mask
 
 iterator mitems*[T](deq: var Deque[T]): var T =
-  ## Yield every element of `deq`.
+  ## Yield every element of `deq`, which can be modified.
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      x = 5*x - 1
+    assert $a == "[49, 99, 149, 199, 249]"
+
   var i = deq.head
   for c in 0 ..< deq.count:
     yield deq.data[i]
@@ -109,18 +222,35 @@ iterator mitems*[T](deq: var Deque[T]): var T =
 
 iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
   ## Yield every (position, value) of `deq`.
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = initDeque[int]()
+  ##   for i in 1 .. 3:
+  ##     a.addLast(10*i)
+  ##
+  ##   for k, v in pairs(a):
+  ##     echo "key: ", k, ", value: ", v
+  ##
+  ##   # key: 0, value: 10
+  ##   # key: 1, value: 20
+  ##   # key: 2, value: 30
+  ##
   var i = deq.head
   for c in 0 ..< deq.count:
     yield (c, deq.data[i])
     i = (i + 1) and deq.mask
 
 proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
-  ## Return true if `item` is in `deq` or false if not found. Usually used
-  ## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``.
+  ## Return true if `item` is in `deq` or false if not found.
+  ##
+  ## Usually used via the ``in`` operator.
+  ## It is the equivalent of ``deq.find(item) >= 0``.
   ##
   ## .. code-block:: Nim
   ##   if x in q:
-  ##     assert q.contains x
+  ##     assert q.contains(x)
   for e in deq:
     if e == item: return true
   return false
@@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
 
 proc addFirst*[T](deq: var Deque[T], item: T) =
   ## Add an `item` to the beginning of the `deq`.
+  ##
+  ## See also:
+  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addFirst(10*i)
+    assert $a == "[50, 40, 30, 20, 10]"
+
   expandIfNeeded(deq)
   inc deq.count
   deq.head = (deq.head - 1) and deq.mask
@@ -145,6 +288,19 @@ proc addFirst*[T](deq: var Deque[T], item: T) =
 
 proc addLast*[T](deq: var Deque[T], item: T) =
   ## Add an `item` to the end of the `deq`.
+  ##
+  ## See also:
+  ## * `addFirst proc <#addFirst,Deque[T],T>`_
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+
   expandIfNeeded(deq)
   inc deq.count
   deq.data[deq.tail] = item
@@ -152,11 +308,41 @@ proc addLast*[T](deq: var Deque[T], item: T) =
 
 proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
   ## Returns the first element of `deq`, but does not remove it from the deque.
+  ##
+  ## See also:
+  ## * `addFirst proc <#addFirst,Deque[T],T>`_
+  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.peekFirst == 10
+    assert len(a) == 5
+
   emptyCheck(deq)
   result = deq.data[deq.head]
 
 proc peekLast*[T](deq: Deque[T]): T {.inline.} =
   ## Returns the last element of `deq`, but does not remove it from the deque.
+  ##
+  ## See also:
+  ## * `addFirst proc <#addFirst,Deque[T],T>`_
+  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.peekLast == 50
+    assert len(a) == 5
+
   emptyCheck(deq)
   result = deq.data[(deq.tail - 1) and deq.mask]
 
@@ -165,6 +351,23 @@ template destroy(x: untyped) =
 
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove and returns the first element of the `deq`.
+  ##
+  ## See also:
+  ## * `addFirst proc <#addFirst,Deque[T],T>`_
+  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## * `popLast proc <#popLast,Deque[T]>`_
+  ## * `clear proc <#clear,Deque[T]>`_
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.popFirst == 10
+    assert $a == "[20, 30, 40, 50]"
+
   emptyCheck(deq)
   dec deq.count
   result = deq.data[deq.head]
@@ -173,6 +376,23 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove and returns the last element of the `deq`.
+  ##
+  ## See also:
+  ## * `addFirst proc <#addFirst,Deque[T],T>`_
+  ## * `addLast proc <#addLast,Deque[T],T>`_
+  ## * `peekFirst proc <#peekFirst,Deque[T]>`_
+  ## * `peekLast proc <#peekLast,Deque[T]>`_
+  ## * `popFirst proc <#popFirst,Deque[T]>`_
+  ## * `clear proc <#clear,Deque[T]>`_
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addLast(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    assert a.popLast == 50
+    assert $a == "[10, 20, 30, 40]"
+
   emptyCheck(deq)
   dec deq.count
   deq.tail = (deq.tail - 1) and deq.mask
@@ -181,17 +401,39 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
 
 proc clear*[T](deq: var Deque[T]) {.inline.} =
   ## Resets the deque so that it is empty.
+  ##
+  ## See also:
+  ## * `clear proc <#clear,Deque[T]>`_
+  ## * `shrink proc <#shrink,Deque[T],int,int>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addFirst(10*i)
+    assert $a == "[50, 40, 30, 20, 10]"
+    clear(a)
+    assert len(a) == 0
+
   for el in mitems(deq): destroy(el)
   deq.count = 0
   deq.tail = deq.head
 
 proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
   ## Remove `fromFirst` elements from the front of the deque and
-  ## `fromLast` elements from the back. If the supplied number of
-  ## elements exceeds the total number of elements in the deque,
-  ## the deque will remain empty.
+  ## `fromLast` elements from the back.
+  ##
+  ## If the supplied number of elements exceeds the total number of elements
+  ## in the deque, the deque will remain empty.
   ##
-  ## Any user defined destructors
+  ## See also:
+  ## * `clear proc <#clear,Deque[T]>`_
+  runnableExamples:
+    var a = initDeque[int]()
+    for i in 1 .. 5:
+      a.addFirst(10*i)
+    assert $a == "[50, 40, 30, 20, 10]"
+    a.shrink(fromFirst = 2, fromLast = 1)
+    assert $a == "[30, 20]"
+
   if fromFirst + fromLast > deq.count:
     clear(deq)
     return
@@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
     result.addQuoted(x)
   result.add("]")
 
+
+
 when isMainModule:
   var deq = initDeque[int](1)
   deq.addLast(4)
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index def96b8f7..9d7950ea6 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -9,9 +9,12 @@
 
 ## The ``intsets`` module implements an efficient int set implemented as a
 ## `sparse bit set`:idx:.
-## **Note**: Since Nim currently does not allow the assignment operator to
-## be overloaded, ``=`` for int sets performs some rather meaningless shallow
-## copy; use ``assign`` to get a deep copy.
+
+## **Note**: Currently the assignment operator ``=`` for ``intsets``
+## performs some rather meaningless shallow copy. Since Nim currently does
+## not allow the assignment operator to be overloaded, use ``assign`` to
+## get a deep copy.
+
 
 import
   hashes, math
@@ -94,7 +97,7 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
   t.data[h] = result
 
 proc contains*(s: IntSet, key: int): bool =
-  ## returns true iff `key` is in `s`.
+  ## Returns true iff `key` is in `s`.
   if s.elems <= s.a.len:
     for i in 0..<s.elems:
       if s.a[i] == key: return true
@@ -107,7 +110,7 @@ proc contains*(s: IntSet, key: int): bool =
       result = false
 
 iterator items*(s: IntSet): int {.inline.} =
-  ## iterates over any included element of `s`.
+  ## Iterates over any included element of `s`.
   if s.elems <= s.a.len:
     for i in 0..<s.elems:
       yield s.a[i]
@@ -135,7 +138,7 @@ proc bitincl(s: var IntSet, key: int) {.inline.} =
       `shl`(1, u and IntMask)
 
 proc incl*(s: var IntSet, key: int) =
-  ## includes an element `key` in `s`.
+  ## Includes an element `key` in `s`.
   if s.elems <= s.a.len:
     for i in 0..<s.elems:
       if s.a[i] == key: return
@@ -170,7 +173,7 @@ proc exclImpl(s: var IntSet, key: int) =
           not `shl`(1, u and IntMask)
 
 proc excl*(s: var IntSet, key: int) =
-  ## excludes `key` from the set `s`.
+  ## Excludes `key` from the set `s`.
   exclImpl(s, key)
 
 proc excl*(s: var IntSet, other: IntSet) =
@@ -178,14 +181,14 @@ proc excl*(s: var IntSet, other: IntSet) =
   for item in other: excl(s, item)
 
 proc missingOrExcl*(s: var IntSet, key: int) : bool =
-  ## returns true if `s` does not contain `key`, otherwise
+  ## Returns true if `s` does not contain `key`, otherwise
   ## `key` is removed from `s` and false is returned.
   var count = s.elems
   exclImpl(s, key)
   result = count == s.elems
 
 proc containsOrIncl*(s: var IntSet, key: int): bool =
-  ## returns true if `s` contains `key`, otherwise `key` is included in `s`
+  ## Returns true if `s` contains `key`, otherwise `key` is included in `s`
   ## and false is returned.
   if s.elems <= s.a.len:
     for i in 0..<s.elems:
@@ -206,23 +209,28 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
       result = false
 
 proc initIntSet*: IntSet =
-  ## creates a new int set that is empty.
-
-  #newSeq(result.data, InitIntSetSize)
-  #result.max = InitIntSetSize-1
-  when defined(nimNoNilSeqs):
-    result.data = @[]
-  else:
-    result.data = nil
-  result.max = 0
-  result.counter = 0
-  result.head = nil
-  result.elems = 0
+  ## Returns an empty IntSet. Example:
+  ##
+  ## .. code-block ::
+  ##   var a = initIntSet()
+  ##   a.incl(2)
+
+  # newSeq(result.data, InitIntSetSize)
+  # result.max = InitIntSetSize-1
+  result = IntSet(
+    elems: 0,
+    counter: 0,
+    max: 0,
+    head: nil,
+    data: when defined(nimNoNilSeqs): @[] else: nil)
+  #  a: array[0..33, int] # profiling shows that 34 elements are enough
 
 proc clear*(result: var IntSet) =
-  #setLen(result.data, InitIntSetSize)
-  #for i in 0..InitIntSetSize-1: result.data[i] = nil
-  #result.max = InitIntSetSize-1
+  ## Clears the IntSet back to an empty state.
+
+  # setLen(result.data, InitIntSetSize)
+  # for i in 0..InitIntSetSize-1: result.data[i] = nil
+  # result.max = InitIntSetSize-1
   when defined(nimNoNilSeqs):
     result.data = @[]
   else:
@@ -304,7 +312,7 @@ proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
   result = difference(s1, s2)
 
 proc disjoint*(s1, s2: IntSet): bool =
-  ## Returns true iff the sets `s1` and `s2` have no items in common.
+  ## Returns true if the sets `s1` and `s2` have no items in common.
   for item in s1:
     if contains(s2, item):
       return false
@@ -320,7 +328,7 @@ proc len*(s: IntSet): int {.inline.} =
       inc(result)
 
 proc card*(s: IntSet): int {.inline.} =
-  ## alias for `len() <#len>` _.
+  ## Alias for `len() <#len>` _.
   result = s.len()
 
 proc `<=`*(s1, s2: IntSet): bool =
@@ -349,12 +357,6 @@ proc `$`*(s: IntSet): string =
   ## The `$` operator for int sets.
   dollarImpl()
 
-proc empty*(s: IntSet): bool {.inline, deprecated.} =
-  ## returns true if `s` is empty. This is safe to call even before
-  ## the set has been initialized with `initIntSet`. Note this never
-  ## worked reliably and so is deprecated.
-  result = s.counter == 0
-
 when isMainModule:
   import sequtils, algorithm
 
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 15ce5d074..182eb8442 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -7,34 +7,112 @@
 #    distribution, for details about the copyright.
 #
 
-## Implementation of singly and doubly linked lists. Because it makes no sense
-## to do so, the 'next' and 'prev' pointers are not hidden from you and can
-## be manipulated directly for efficiency.
+## Implementation of:
+## * `singly linked lists <#SinglyLinkedList>`_
+## * `doubly linked lists <#DoublyLinkedList>`_
+## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists)
+## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists)
+##
+##
+## Basic Usage
+## ===========
+##
+## Because it makes no sense to do otherwise, the `next` and `prev` pointers
+## are not hidden from you and can be manipulated directly for efficiency.
+##
+## Lists
+## -----
+##
+## .. code-block::
+##   import lists
+##
+##   var
+##     l = initDoublyLinkedList[int]()
+##     a = newDoublyLinkedNode[int](3)
+##     b = newDoublyLinkedNode[int](7)
+##     c = newDoublyLinkedNode[int](9)
+##
+##   l.append(a)
+##   l.append(b)
+##   l.prepend(c)
+##
+##   assert a.next == b
+##   assert a.prev == c
+##   assert c.next == a
+##   assert c.next.next == b
+##   assert c.prev == nil
+##   assert b.next == nil
+##
+##
+## Rings
+## -----
+##
+## .. code-block::
+##   import lists
+##
+##   var
+##     l = initSinglyLinkedRing[int]()
+##     a = newSinglyLinkedNode[int](3)
+##     b = newSinglyLinkedNode[int](7)
+##     c = newSinglyLinkedNode[int](9)
+##
+##   l.append(a)
+##   l.append(b)
+##   l.prepend(c)
+##
+##   assert c.next == a
+##   assert a.next == b
+##   assert c.next.next == b
+##   assert b.next == c
+##   assert c.next.next.next == c
+##
+## See also
+## ========
+##
+## * `deques module <#deques.html>`_ for double-ended queues
+## * `sharedlist module <#sharedlist.html>`_ for shared singly-linked lists
+
 
 when not defined(nimhygiene):
   {.pragma: dirty.}
 
 type
-  DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
+  DoublyLinkedNodeObj*[T] = object ## A node a doubly linked list consists of.
+    ##
+    ## It consists of a `value` field, and pointers to `next` and `prev`.
     next*, prev*: ref DoublyLinkedNodeObj[T]
     value*: T
   DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
 
-  SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
+  SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
+    ##
+    ## It consists of a `value` field, and a pointer to `next`.
     next*: ref SinglyLinkedNodeObj[T]
     value*: T
   SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
 
-  SinglyLinkedList*[T] = object ## a singly linked list
+  SinglyLinkedList*[T] = object ## A singly linked list.
+    ##
+    ## Use `initSinglyLinkedList proc <#initSinglyLinkedList,>`_ to create
+    ## a new empty list.
     head*, tail*: SinglyLinkedNode[T]
 
-  DoublyLinkedList*[T] = object ## a doubly linked list
+  DoublyLinkedList*[T] = object ## A doubly linked list.
+    ##
+    ## Use `initDoublyLinkedList proc <#initDoublyLinkedList,>`_ to create
+    ## a new empty list.
     head*, tail*: DoublyLinkedNode[T]
 
-  SinglyLinkedRing*[T] = object ## a singly linked ring
+  SinglyLinkedRing*[T] = object ## A singly linked ring.
+    ##
+    ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing,>`_ to create
+    ## a new empty ring.
     head*, tail*: SinglyLinkedNode[T]
 
-  DoublyLinkedRing*[T] = object ## a doubly linked ring
+  DoublyLinkedRing*[T] = object ## A doubly linked ring.
+    ##
+    ## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing,>`_ to create
+    ## a new empty ring.
     head*: DoublyLinkedNode[T]
 
   SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
@@ -46,28 +124,44 @@ type
   SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
 
 proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
-  ## creates a new singly linked list that is empty.
+  ## Creates a new singly linked list that is empty.
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
   discard
 
 proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
-  ## creates a new doubly linked list that is empty.
+  ## Creates a new doubly linked list that is empty.
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
   discard
 
 proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
-  ## creates a new singly linked ring that is empty.
+  ## Creates a new singly linked ring that is empty.
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
   discard
 
 proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
-  ## creates a new doubly linked ring that is empty.
+  ## Creates a new doubly linked ring that is empty.
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
   discard
 
 proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
-  ## creates a new doubly linked node with the given `value`.
+  ## Creates a new doubly linked node with the given `value`.
+  runnableExamples:
+    var n = newDoublyLinkedNode[int](5)
+    assert n.value == 5
+
   new(result)
   result.value = value
 
 proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
-  ## creates a new singly linked node with the given `value`.
+  ## Creates a new singly linked node with the given `value`.
+  runnableExamples:
+    var n = newSinglyLinkedNode[int](5)
+    assert n.value == 5
+
   new(result)
   result.value = value
 
@@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
       if it == L.head: break
 
 iterator items*[T](L: SomeLinkedList[T]): T =
-  ## yields every value of `L`.
+  ## Yields every value of `L`.
+  ##
+  ## See also:
+  ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
+  ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = initSinglyLinkedList[int]()
+  ##   for i in 1 .. 3:
+  ##     a.append(10*i)
+  ##
+  ##   for x in a:  # the same as: for x in items(a):
+  ##     echo x
+  ##
+  ##   # 10
+  ##   # 20
+  ##   # 30
   itemsListImpl()
 
 iterator items*[T](L: SomeLinkedRing[T]): T =
-  ## yields every value of `L`.
+  ## Yields every value of `L`.
+  ##
+  ## See also:
+  ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
+  ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = initSinglyLinkedRing[int]()
+  ##   for i in 1 .. 3:
+  ##     a.append(10*i)
+  ##
+  ##   for x in a:  # the same as: for x in items(a):
+  ##     echo x
+  ##
+  ##   # 10
+  ##   # 20
+  ##   # 30
   itemsRingImpl()
 
 iterator mitems*[T](L: var SomeLinkedList[T]): var T =
-  ## yields every value of `L` so that you can modify it.
+  ## Yields every value of `L` so that you can modify it.
+  ##
+  ## See also:
+  ## * `items iterator <#items.i,SomeLinkedList[T]>`_
+  ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    for i in 1 .. 5:
+      a.append(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      x = 5*x - 1
+    assert $a == "[49, 99, 149, 199, 249]"
   itemsListImpl()
 
 iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
-  ## yields every value of `L` so that you can modify it.
+  ## Yields every value of `L` so that you can modify it.
+  ##
+  ## See also:
+  ## * `items iterator <#items.i,SomeLinkedRing[T]>`_
+  ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
+    for i in 1 .. 5:
+      a.append(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in mitems(a):
+      x = 5*x - 1
+    assert $a == "[49, 99, 149, 199, 249]"
   itemsRingImpl()
 
 iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
+  ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
+  ##
+  ## See also:
+  ## * `items iterator <#items.i,SomeLinkedList[T]>`_
+  ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
+    for i in 1 .. 5:
+      a.append(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in nodes(a):
+      if x.value == 30:
+        a.remove(x)
+      else:
+        x.value = 5*x.value - 1
+    assert $a == "[49, 99, 199, 249]"
+
   var it = L.head
   while it != nil:
     var nxt = it.next
@@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
     it = nxt
 
 iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
+  ## Iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
+  ##
+  ## See also:
+  ## * `items iterator <#items.i,SomeLinkedRing[T]>`_
+  ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
+    for i in 1 .. 5:
+      a.append(10*i)
+    assert $a == "[10, 20, 30, 40, 50]"
+    for x in nodes(a):
+      if x.value == 30:
+        a.remove(x)
+      else:
+        x.value = 5*x.value - 1
+    assert $a == "[49, 99, 199, 249]"
+
   var it = L.head
   if it != nil:
     while true:
@@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
       if it == L.head: break
 
 proc `$`*[T](L: SomeLinkedCollection[T]): string =
-  ## turns a list into its string representation.
+  ## Turns a list into its string representation for logging and printing.
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
@@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
   result.add("]")
 
 proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
+  ## Searches in the list for a value. Returns `nil` if the value does not
   ## exist.
+  ##
+  ## See also:
+  ## * `contains proc <#contains,SomeLinkedCollection[T],T>`_
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.find(9).value == 9
+    assert a.find(1) == nil
+
   for x in nodes(L):
     if x.value == value: return x
 
 proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
+  ## Searches in the list for a value. Returns `false` if the value does not
+  ## exist, `true` otherwise.
+  ##
+  ## See also:
+  ## * `find proc <#find,SomeLinkedCollection[T],T>`_
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
+    assert 8 in a
+    assert(not a.contains(1))
+    assert 2 notin a
+
   result = find(L, value) != nil
 
 proc append*[T](L: var SinglyLinkedList[T],
                 n: SinglyLinkedNode[T]) {.inline.} =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
+  runnableExamples:
+    var
+      a = initSinglyLinkedList[int]()
+      n = newSinglyLinkedNode[int](9)
+    a.append(n)
+    assert a.contains(9)
+
   n.next = nil
   if L.tail != nil:
     assert(L.tail.next == nil)
@@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
   if L.head == nil: L.head = n
 
 proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedList[T],
                  n: SinglyLinkedNode[T]) {.inline.} =
-  ## prepends a node to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
+  runnableExamples:
+    var
+      a = initSinglyLinkedList[int]()
+      n = newSinglyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   n.next = L.head
   L.head = n
   if L.tail == nil: L.tail = n
 
 proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
-  ## prepends a node to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  runnableExamples:
+    var a = initSinglyLinkedList[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newSinglyLinkedNode(value))
 
+
+
 proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var
+      a = initDoublyLinkedList[int]()
+      n = newDoublyLinkedNode[int](9)
+    a.append(n)
+    assert a.contains(9)
+
   n.next = nil
   n.prev = L.tail
   if L.tail != nil:
@@ -176,11 +450,40 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if L.head == nil: L.head = n
 
 proc append*[T](L: var DoublyLinkedList[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var
+      a = initDoublyLinkedList[int]()
+      n = newDoublyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   n.prev = nil
   n.next = L.head
   if L.head != nil:
@@ -190,18 +493,56 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if L.tail == nil: L.tail = n
 
 proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedList[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newDoublyLinkedNode(value))
 
 proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
-  ## removes `n` from `L`. Efficiency: O(1).
+  ## Removes a node `n` from `L`. Efficiency: O(1).
+  runnableExamples:
+    var
+      a = initDoublyLinkedList[int]()
+      n = newDoublyLinkedNode[int](5)
+    a.append(n)
+    assert 5 in a
+    a.remove(n)
+    assert 5 notin a
+
   if n == L.tail: L.tail = n.prev
   if n == L.head: L.head = n.next
   if n.next != nil: n.next.prev = n.prev
   if n.prev != nil: n.prev.next = n.next
 
+
+
 proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
+  runnableExamples:
+    var
+      a = initSinglyLinkedRing[int]()
+      n = newSinglyLinkedNode[int](9)
+    a.append(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
@@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
     L.tail = n
 
 proc append*[T](L: var SinglyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newSinglyLinkedNode(value))
 
 proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
+  runnableExamples:
+    var
+      a = initSinglyLinkedRing[int]()
+      n = newSinglyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     assert(L.tail != nil)
@@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   L.head = n
 
 proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
+  ##   for prepending a node
+  runnableExamples:
+    var a = initSinglyLinkedRing[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newSinglyLinkedNode(value))
 
+
+
 proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
-  ## appends a node `n` to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var
+      a = initDoublyLinkedRing[int]()
+      n = newDoublyLinkedNode[int](9)
+    a.append(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
     L.head = n
 
 proc append*[T](L: var DoublyLinkedRing[T], value: T) =
-  ## appends a value to `L`. Efficiency: O(1).
+  ## Appends (adds to the end) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
+    a.append(9)
+    a.append(8)
+    assert a.contains(9)
   append(L, newDoublyLinkedNode(value))
 
 proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
-  ## prepends a node `n` to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
+  ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var
+      a = initDoublyLinkedRing[int]()
+      n = newDoublyLinkedNode[int](9)
+    a.prepend(n)
+    assert a.contains(9)
+
   if L.head != nil:
     n.next = L.head
     n.prev = L.head.prev
@@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
   L.head = n
 
 proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
-  ## prepends a value to `L`. Efficiency: O(1).
+  ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
+  ##
+  ## See also:
+  ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for appending a node
+  ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
+  ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for prepending a node
+  ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
+  ##   for removing a node
+  runnableExamples:
+    var a = initDoublyLinkedRing[int]()
+    a.prepend(9)
+    a.prepend(8)
+    assert a.contains(9)
   prepend(L, newDoublyLinkedNode(value))
 
 proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
-  ## removes `n` from `L`. Efficiency: O(1).
+  ## Removes `n` from `L`. Efficiency: O(1).
+  runnableExamples:
+    var
+      a = initDoublyLinkedRing[int]()
+      n = newDoublyLinkedNode[int](5)
+    a.append(n)
+    assert 5 in a
+    a.remove(n)
+    assert 5 notin a
+
   n.next.prev = n.prev
   n.prev.next = n.next
   if n == L.head:
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
deleted file mode 100644
index 9a1d169fb..000000000
--- a/lib/pure/collections/queues.nim
+++ /dev/null
@@ -1,257 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Implementation of a `queue`:idx:. The underlying implementation uses a ``seq``.
-##
-## None of the procs that get an individual value from the queue can be used
-## on an empty queue.
-## If compiled with `boundChecks` option, those procs will raise an `IndexError`
-## on such access. This should not be relied upon, as `-d:release` will
-## disable those checks and may return garbage or crash the program.
-##
-## As such, a check to see if the queue is empty is needed before any
-## access, unless your program logic guarantees it indirectly.
-##
-## .. code-block:: Nim
-##   proc foo(a, b: Positive) =  # assume random positive values for `a` and `b`
-##     var q = initQueue[int]()  # initializes the object
-##     for i in 1 ..< a: q.add i  # populates the queue
-##
-##     if b < q.len:  # checking before indexed access
-##       echo "The element at index position ", b, " is ", q[b]
-##
-##     # The following two lines don't need any checking on access due to the
-##     # logic of the program, but that would not be the case if `a` could be 0.
-##     assert q.front == 1
-##     assert q.back == a
-##
-##     while q.len > 0:  # checking if the queue is empty
-##       echo q.pop()
-##
-## Note: For inter thread communication use
-## a `Channel <channels.html>`_ instead.
-
-import math
-
-{.warning: "`queues` module is deprecated - use `deques` instead".}
-
-type
-  Queue* {.deprecated.} [T] = object ## A queue.
-    data: seq[T]
-    rd, wr, count, mask: int
-
-proc initQueue*[T](initialSize: int = 4): Queue[T] =
-  ## Create a new queue.
-  ## Optionally, the initial capacity can be reserved via `initialSize` as a
-  ## performance optimization. The length of a newly created queue will still
-  ## be 0.
-  ##
-  ## `initialSize` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module.
-  assert isPowerOfTwo(initialSize)
-  result.mask = initialSize-1
-  newSeq(result.data, initialSize)
-
-proc len*[T](q: Queue[T]): int {.inline.}=
-  ## Return the number of elements of `q`.
-  result = q.count
-
-template emptyCheck(q) =
-  # Bounds check for the regular queue access.
-  when compileOption("boundChecks"):
-    if unlikely(q.count < 1):
-      raise newException(IndexError, "Empty queue.")
-
-template xBoundsCheck(q, i) =
-  # Bounds check for the array like accesses.
-  when compileOption("boundChecks"):  # d:release should disable this.
-    if unlikely(i >= q.count):  # x < q.low is taken care by the Natural parameter
-      raise newException(IndexError,
-                         "Out of bounds: " & $i & " > " & $(q.count - 1))
-
-proc front*[T](q: Queue[T]): T {.inline.}=
-  ## Return the oldest element of `q`. Equivalent to `q.pop()` but does not
-  ## remove it from the queue.
-  emptyCheck(q)
-  result = q.data[q.rd]
-
-proc back*[T](q: Queue[T]): T {.inline.} =
-  ## Return the newest element of `q` but does not remove it from the queue.
-  emptyCheck(q)
-  result = q.data[q.wr - 1 and q.mask]
-
-proc `[]`*[T](q: Queue[T], i: Natural) : T {.inline.} =
-  ## Access the i-th element of `q` by order of insertion.
-  ## q[0] is the oldest (the next one q.pop() will extract),
-  ## q[^1] is the newest (last one added to the queue).
-  xBoundsCheck(q, i)
-  return q.data[q.rd + i and q.mask]
-
-proc `[]`*[T](q: var Queue[T], i: Natural): var T {.inline.} =
-  ## Access the i-th element of `q` and returns a mutable
-  ## reference to it.
-  xBoundsCheck(q, i)
-  return q.data[q.rd + i and q.mask]
-
-proc `[]=`* [T] (q: var Queue[T], i: Natural, val : T) {.inline.} =
-  ## Change the i-th element of `q`.
-  xBoundsCheck(q, i)
-  q.data[q.rd + i and q.mask] = val
-
-iterator items*[T](q: Queue[T]): T =
-  ## Yield every element of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield q.data[i]
-    i = (i + 1) and q.mask
-
-iterator mitems*[T](q: var Queue[T]): var T =
-  ## Yield every element of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield q.data[i]
-    i = (i + 1) and q.mask
-
-iterator pairs*[T](q: Queue[T]): tuple[key: int, val: T] =
-  ## Yield every (position, value) of `q`.
-  var i = q.rd
-  for c in 0 ..< q.count:
-    yield (c, q.data[i])
-    i = (i + 1) and q.mask
-
-proc contains*[T](q: Queue[T], item: T): bool {.inline.} =
-  ## Return true if `item` is in `q` or false if not found. Usually used
-  ## via the ``in`` operator. It is the equivalent of ``q.find(item) >= 0``.
-  ##
-  ## .. code-block:: Nim
-  ##   if x in q:
-  ##     assert q.contains x
-  for e in q:
-    if e == item: return true
-  return false
-
-proc add*[T](q: var Queue[T], item: T) =
-  ## Add an `item` to the end of the queue `q`.
-  var cap = q.mask+1
-  if unlikely(q.count >= cap):
-    var n = newSeq[T](cap*2)
-    for i, x in pairs(q):  # don't use copyMem because the GC and because it's slower.
-      shallowCopy(n[i], x)
-    shallowCopy(q.data, n)
-    q.mask = cap*2 - 1
-    q.wr = q.count
-    q.rd = 0
-  inc q.count
-  q.data[q.wr] = item
-  q.wr = (q.wr + 1) and q.mask
-
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
-
-proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
-  ## Remove and returns the first (oldest) element of the queue `q`.
-  emptyCheck(q)
-  dec q.count
-  result = q.data[q.rd]
-  q.data[q.rd] = default(type(result))
-  q.rd = (q.rd + 1) and q.mask
-
-proc enqueue*[T](q: var Queue[T], item: T) =
-  ## Alias for the ``add`` operation.
-  q.add(item)
-
-proc dequeue*[T](q: var Queue[T]): T =
-  ## Alias for the ``pop`` operation.
-  q.pop()
-
-proc `$`*[T](q: Queue[T]): string =
-  ## Turn a queue into its string representation.
-  result = "["
-  for x in items(q):  # Don't remove the items here for reasons that don't fit in this margin.
-    if result.len > 1: result.add(", ")
-    result.add($x)
-  result.add("]")
-
-when isMainModule:
-  var q = initQueue[int](1)
-  q.add(123)
-  q.add(9)
-  q.enqueue(4)
-  var first = q.dequeue()
-  q.add(56)
-  q.add(6)
-  var second = q.pop()
-  q.add(789)
-
-  assert first == 123
-  assert second == 9
-  assert($q == "[4, 56, 6, 789]")
-
-  assert q[0] == q.front and q.front == 4
-  q[0] = 42
-  q[q.len - 1] = 7
-
-  assert 6 in q and 789 notin q
-  assert q.find(6) >= 0
-  assert q.find(789) < 0
-
-  for i in -2 .. 10:
-    if i in q:
-      assert q.contains(i) and q.find(i) >= 0
-    else:
-      assert(not q.contains(i) and q.find(i) < 0)
-
-  when compileOption("boundChecks"):
-    try:
-      echo q[99]
-      assert false
-    except IndexError:
-      discard
-
-    try:
-      assert q.len == 4
-      for i in 0 ..< 5: q.pop()
-      assert false
-    except IndexError:
-      discard
-
-  # grabs some types of resize error.
-  q = initQueue[int]()
-  for i in 1 .. 4: q.add i
-  q.pop()
-  q.pop()
-  for i in 5 .. 8: q.add i
-  assert $q == "[3, 4, 5, 6, 7, 8]"
-
-  # Similar to proc from the documentation example
-  proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
-    var q = initQueue[int]()
-    assert q.len == 0
-    for i in 1 .. a: q.add i
-
-    if b < q.len: # checking before indexed access.
-      assert q[b] == b + 1
-
-    # The following two lines don't need any checking on access due to the logic
-    # of the program, but that would not be the case if `a` could be 0.
-    assert q.front == 1
-    assert q.back == a
-
-    while q.len > 0: # checking if the queue is empty
-      assert q.pop() > 0
-
-  #foo(0,0)
-  foo(8,5)
-  foo(10,9)
-  foo(1,1)
-  foo(2,1)
-  foo(1,5)
-  foo(3,2)
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 39ba6df49..b0d50bce2 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -7,16 +7,71 @@
 #    distribution, for details about the copyright.
 #
 
-## :Author: Alexander Mitchell-Robinson (Amrykid)
+## Although this module has ``seq`` in its name, it implements operations
+## not only for `seq`:idx: type, but for three built-in container types under
+## the ``openArray`` umbrella:
+## * sequences
+## * strings
+## * array
 ##
-## This module implements operations for the built-in `seq`:idx: type which
-## were inspired by functional programming languages.
+## The system module defines several common functions, such as:
+## * ``newseq[T]`` for creating new sequences of type ``T``
+## * ``@`` for converting arrays and strings to sequences
+## * ``add`` for adding new elements to strings and sequences
+## * ``&`` for string and seq concatenation
+## * ``in`` (alias for ``contains``) and ``notin`` for checking if an item is
+##   in a container
 ##
-## For functional style programming you may want to pass `anonymous procs
-## <manual.html#procedures-anonymous-procs>`_ to procs like ``filter`` to
-## reduce typing. Anonymous procs can use `the special do notation
-## <manual.html#procedures-do-notation>`_
-## which is more convenient in certain situations.
+## This module builds upon that, providing additional functionality in form of
+## procs, iterators and templates inspired by functional programming
+## languages.
+##
+## For functional style programming you have different options at your disposal:
+## * pass `anonymous proc<manual.html#procedures-anonymous-procs>`_
+## * import `sugar module<sugar.html>`_  and use
+##   `=> macro<sugar.html#%3D>.m,untyped,untyped>`_
+## * use `...It templates<#18>`_
+##   (`mapIt<#mapIt.t,typed,untyped>`_,
+##   `filterIt<#filterIt.t,untyped,untyped>`_, etc.)
+##
+## The chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procs-method-call-syntax>`_.
+##
+## .. code-block::
+##   import sequtils, sugar
+##
+##   # Creating a sequence from 1 to 10, multiplying each member by 2,
+##   # keeping only the members which are not divisible by 6.
+##   let
+##     foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0)
+##     bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0)
+##
+##   doAssert foo == bar
+##   echo foo                  # @[2, 4, 8, 10, 14, 16, 20]
+##
+##   echo foo.any(x => x > 17) # true
+##   echo bar.allIt(it < 20)   # false
+##   echo foo.foldl(a + b)     # 74; sum of all members
+##
+## .. code-block::
+##   import sequtils
+##   from strutils import join
+##
+##   let
+##     vowels = @"aeiou" # creates a sequence @['a', 'e', 'i', 'o', 'u']
+##     foo = "sequtils is an awesome module"
+##
+##   echo foo.filterIt(it notin vowels).join # "sqtls s n wsm mdl"
+##
+## ----
+##
+## **See also**:
+## * `strutils module<strutils.html>`_ for common string functions
+## * `sugar module<sugar.html>`_ for syntactic sugar macros
+## * `algorithm module<algorithm.html>`_ for common generic algorithms
+## * `json module<json.html>`_ for a structure which allows
+##   heterogeneous members
+
 
 include "system/inclrtl"
 
@@ -31,7 +86,7 @@ macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped
   ##  substitution in macro arguments such as
   ## https://github.com/nim-lang/Nim/issues/7187
   ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp``
-  ## except when ``letAssigneable`` is false (eg to handle openArray) where
+  ## except when ``letAssigneable`` is false (e.g. to handle openArray) where
   ## it just forwards ``exp`` unchanged
   expectKind(expAlias, nnkIdent)
   var val = exp
@@ -49,16 +104,20 @@ macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped
 
 proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
   ## Takes several sequences' items and returns them inside a new sequence.
+  ## All sequences must be of the same type.
   ##
-  ## Example:
+  ## See also:
+  ## * `distribute proc<#distribute,seq[T],Positive>`_ for a reverse
+  ##   operation
   ##
-  ## .. code-block::
-  ##   let
-  ##     s1 = @[1, 2, 3]
-  ##     s2 = @[4, 5]
-  ##     s3 = @[6, 7]
-  ##     total = concat(s1, s2, s3)
-  ##   assert total == @[1, 2, 3, 4, 5, 6, 7]
+  runnableExamples:
+    let
+      s1 = @[1, 2, 3]
+      s2 = @[4, 5]
+      s3 = @[6, 7]
+      total = concat(s1, s2, s3)
+    assert total == @[1, 2, 3, 4, 5, 6, 7]
+
   var L = 0
   for seqitm in items(seqs): inc(L, len(seqitm))
   newSeq(result, L)
@@ -71,13 +130,14 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
 proc count*[T](s: openArray[T], x: T): int =
   ## Returns the number of occurrences of the item `x` in the container `s`.
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let
-  ##     s = @[1, 2, 2, 3, 2, 4, 2]
-  ##     c = count(s, 2)
-  ##   assert c == 4
+  runnableExamples:
+    let
+      a = @[1, 2, 2, 3, 2, 4, 2]
+      b = "abracadabra"
+    assert count(a, 2) == 4
+    assert count(a, 99) == 0
+    assert count(b, 'r') == 2
+
   for itm in items(s):
     if itm == x:
       inc result
@@ -85,15 +145,14 @@ proc count*[T](s: openArray[T], x: T): int =
 proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
   ## Returns a new sequence with the items of the container `s` repeated
   ## `n` times.
+  ## `n` must be a non-negative number (zero or more).
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   let
-  ##     s = @[1, 2, 3]
-  ##     total = s.cycle(3)
-  ##   assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+  runnableExamples:
+    let
+      s = @[1, 2, 3]
+      total = s.cycle(3)
+    assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
+
   result = newSeq[T](n * s.len)
   var o = 0
   for x in 0 ..< n:
@@ -103,14 +162,13 @@ proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
 
 proc repeat*[T](x: T, n: Natural): seq[T] =
   ## Returns a new sequence with the item `x` repeated `n` times.
+  ## `n` must be a non-negative number (zero or more).
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   let
-  ##     total = repeat(5, 3)
-  ##   assert total == @[5, 5, 5]
+  runnableExamples:
+    let
+      total = repeat(5, 3)
+    assert total == @[5, 5, 5]
+
   result = newSeq[T](n)
   for i in 0 ..< n:
     result[i] = x
@@ -118,16 +176,18 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
 proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
   ## Returns a new sequence without duplicates.
   ##
-  ## Example:
+  ## Setting the optional argument ``isSorted`` to ``true`` (default: false)
+  ## uses a faster algorithm for deduplication.
   ##
-  ## .. code-block::
-  ##   let
-  ##     dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
-  ##     dup2 = @["a", "a", "c", "d", "d"]
-  ##     unique1 = deduplicate(dup1)
-  ##     unique2 = deduplicate(dup2)
-  ##   assert unique1 == @[1, 3, 4, 2, 8]
-  ##   assert unique2 == @["a", "c", "d"]
+  runnableExamples:
+    let
+      dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
+      dup2 = @["a", "a", "c", "d", "d"]
+      unique1 = deduplicate(dup1)
+      unique2 = deduplicate(dup2, isSorted = true)
+    assert unique1 == @[1, 3, 4, 2, 8]
+    assert unique2 == @["a", "c", "d"]
+
   result = @[]
   if s.len > 0:
     if isSorted:
@@ -144,39 +204,44 @@ proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] =
 proc zip*[S, T](s1: openArray[S], s2: openArray[T]): seq[tuple[a: S, b: T]] =
   ## Returns a new sequence with a combination of the two input containers.
   ##
-  ## For convenience you can access the returned tuples through the named
-  ## fields `a` and `b`. If one container is shorter, the remaining items in
-  ## the longer container are discarded.
+  ## The input containers can be of different types.
+  ## If one container is shorter, the remaining items in the longer container
+  ## are discarded.
   ##
-  ## Example:
+  ## For convenience you can access the returned tuples through the named
+  ## fields `a` and `b`.
   ##
-  ## .. code-block::
-  ##   let
-  ##     short = @[1, 2, 3]
-  ##     long = @[6, 5, 4, 3, 2, 1]
-  ##     words = @["one", "two", "three"]
-  ##     zip1 = zip(short, long)
-  ##     zip2 = zip(short, words)
-  ##   assert zip1 == @[(1, 6), (2, 5), (3, 4)]
-  ##   assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
-  ##   assert zip1[2].b == 4
-  ##   assert zip2[2].b == "three"
+  runnableExamples:
+    let
+      short = @[1, 2, 3]
+      long = @[6, 5, 4, 3, 2, 1]
+      words = @["one", "two", "three"]
+      letters = "abcd"
+      zip1 = zip(short, long)
+      zip2 = zip(short, words)
+      zip3 = zip(long, letters)
+    assert zip1 == @[(1, 6), (2, 5), (3, 4)]
+    assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
+    assert zip3 == @[(a: 6, b: 'a'), (a: 5, b: 'b'), (a: 4, b: 'c'),
+                     (a: 3, b: 'd')]
+    assert zip1[2].b == 4
+    assert zip2[2].b == "three"
+
   var m = min(s1.len, s2.len)
   newSeq(result, m)
   for i in 0 ..< m:
     result[i] = (s1[i], s2[i])
 
 proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
-  ## Splits and distributes a sequence `s` into `num` sub sequences.
+  ## Splits and distributes a sequence `s` into `num` sub-sequences.
   ##
-  ## Returns a sequence of `num` sequences. For some input values this is the
-  ## inverse of the `concat <#concat>`_ proc. The proc will assert in debug
-  ## builds if `s` is nil or `num` is less than one, and will likely crash on
-  ## release builds.  The input sequence `s` can be empty, which will produce
+  ## Returns a sequence of `num` sequences. For *some* input values this is the
+  ## inverse of the `concat <#concat,varargs[seq[T]]>`_ proc.
+  ## The input sequence `s` can be empty, which will produce
   ## `num` empty sequences.
   ##
   ## If `spread` is false and the length of `s` is not a multiple of `num`, the
-  ## proc will max out the first sub sequences with ``1 + len(s) div num``
+  ## proc will max out the first sub-sequence with ``1 + len(s) div num``
   ## entries, leaving the remainder of elements to the last sequence.
   ##
   ## On the other hand, if `spread` is true, the proc will distribute evenly
@@ -184,18 +249,16 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   ## more suited to multithreading where you are passing equal sized work units
   ## to a thread pool and want to maximize core usage.
   ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let numbers = @[1, 2, 3, 4, 5, 6, 7]
-  ##   assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-  ##   assert numbers.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-  ##   assert numbers.distribute(6)[0] == @[1, 2]
-  ##   assert numbers.distribute(6)[5] == @[7]
+  runnableExamples:
+    let numbers = @[1, 2, 3, 4, 5, 6, 7]
+    assert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    assert numbers.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    assert numbers.distribute(6)[0] == @[1, 2]
+    assert numbers.distribute(6)[1] == @[3]
+
   if num < 2:
     result = @[s]
     return
-
   let num = int(num) # XXX probably only needed because of .. bug
 
   # Create the result and calculate the stride size and the remainder if any.
@@ -209,13 +272,11 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
   if extra == 0 or spread == false:
     # Use an algorithm which overcounts the stride and minimizes reading limits.
     if extra > 0: inc(stride)
-
     for i in 0 ..< num:
       result[i] = newSeq[T]()
       for g in first ..< min(s.len, first + stride):
         result[i].add(s[g])
       first += stride
-
   else:
     # Use an undercounting algorithm which *adds* the remainder each iteration.
     for i in 0 ..< num:
@@ -223,7 +284,6 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
       if extra > 0:
         extra -= 1
         inc(last)
-
       result[i] = newSeq[T]()
       for g in first ..< last:
         result[i].add(s[g])
@@ -231,110 +291,103 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
 
 proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}):
                                                             seq[S]{.inline.} =
-  ## Returns a new sequence with the results of `op` applied to every item in
-  ## the container `s`.
+  ## Returns a new sequence with the results of `op` proc applied to every
+  ## item in the container `s`.
   ##
-  ## Since the input is not modified you can use this version of ``map`` to
+  ## Since the input is not modified you can use it to
   ## transform the type of the elements in the input container.
   ##
-  ## Example:
+  ## See also:
+  ## * `mapIt template<#mapIt.t,typed,untyped>`_
+  ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version
   ##
-  ## .. code-block:: nim
-  ##   let
-  ##     a = @[1, 2, 3, 4]
-  ##     b = map(a, proc(x: int): string = $x)
-  ##   assert b == @["1", "2", "3", "4"]
+  runnableExamples:
+    let
+      a = @[1, 2, 3, 4]
+      b = map(a, proc(x: int): string = $x)
+    assert b == @["1", "2", "3", "4"]
+
   newSeq(result, s.len)
   for i in 0 ..< s.len:
     result[i] = op(s[i])
 
-proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
-                                                              {.deprecated.} =
-  ## Applies `op` to every item in `s` modifying it directly.
-  ##
-  ## Note that this version of ``map`` requires your input and output types to
-  ## be the same, since they are modified in-place.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   map(a, proc(x: var string) = x &= "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
-  ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
-  for i in 0 ..< s.len: op(s[i])
-
 proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
                                                               {.inline.} =
   ## Applies `op` to every item in `s` modifying it directly.
   ##
-  ## Note that this requires your input and output types to
-  ## be the same, since they are modified in-place.
+  ## Note that container `s` must be declared as a ``var``
+  ## and it is required for your input and output types to
+  ## be the same, since `s` is modified in-place.
   ## The parameter function takes a ``var T`` type parameter.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   apply(a, proc(x: var string) = x &= "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
+  ## See also:
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_
+  ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
+  runnableExamples:
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: var string) = x &= "42")
+    assert a == @["142", "242", "342", "442"]
+
   for i in 0 ..< s.len: op(s[i])
 
 proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
                                                               {.inline.} =
   ## Applies `op` to every item in `s` modifying it directly.
   ##
-  ## Note that this requires your input and output types to
-  ## be the same, since they are modified in-place.
+  ## Note that container `s` must be declared as a ``var``
+  ## and it is required for your input and output types to
+  ## be the same, since `s` is modified in-place.
   ## The parameter function takes and returns a ``T`` type variable.
   ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var a = @["1", "2", "3", "4"]
-  ##   echo repr(a)
-  ##   # --> ["1", "2", "3", "4"]
-  ##   apply(a, proc(x: string): string = x & "42")
-  ##   echo repr(a)
-  ##   # --> ["142", "242", "342", "442"]
+  ## See also:
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_
+  ## * `map proc<#map,openArray[T],proc(T)>`_
   ##
+  runnableExamples:
+    var a = @["1", "2", "3", "4"]
+    apply(a, proc(x: string): string = x & "42")
+    assert a == @["142", "242", "342", "442"]
+
   for i in 0 ..< s.len: s[i] = op(s[i])
 
 iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
-  ## Iterates through a container and yields every item that fulfills the
-  ## predicate.
+  ## Iterates through a container `s` and yields every item that fulfills the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## See also:
+  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
-  ##     echo($n)
-  ##   # echoes 4, 8, 4 in separate lines
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    var evens = newSeq[int]()
+    for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
+      evens.add(n)
+    assert evens == @[4, 8, 4]
+
   for i in 0 ..< s.len:
     if pred(s[i]):
       yield s[i]
 
 proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
                                                                   {.inline.} =
-  ## Returns a new sequence with all the items that fulfilled the predicate.
+  ## Returns a new sequence with all the items of `s` that fulfilled the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## See also:
+  ## * `filterIt template<#filterIt.t,untyped,untyped>`_
+  ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
+  ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version
   ##
-  ## .. code-block::
-  ##   let
-  ##     colors = @["red", "yellow", "black"]
-  ##     f1 = filter(colors, proc(x: string): bool = x.len < 6)
-  ##     f2 = filter(colors) do (x: string) -> bool : x.len > 5
-  ##   assert f1 == @["red", "black"]
-  ##   assert f2 == @["yellow"]
+  runnableExamples:
+    let
+      colors = @["red", "yellow", "black"]
+      f1 = filter(colors, proc(x: string): bool = x.len < 6)
+      f2 = filter(colors, proc(x: string): bool = x.contains('y'))
+    assert f1 == @["red", "black"]
+    assert f2 == @["yellow"]
+
   result = newSeq[T]()
   for i in 0 ..< s.len:
     if pred(s[i]):
@@ -342,15 +395,23 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
 
 proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
                                                                 {.inline.} =
-  ## Keeps the items in the passed sequence if they fulfilled the predicate.
-  ## Same as the ``filter`` proc, but modifies the sequence directly.
+  ## Keeps the items in the passed sequence `s` if they fulfilled the
+  ## predicate `pred` (function that returns a `bool`).
   ##
-  ## Example:
+  ## Note that `s` must be declared as a ``var``.
   ##
-  ## .. code-block::
-  ##   var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
-  ##   keepIf(floats, proc(x: float): bool = x > 10)
-  ##   assert floats == @[13.0, 12.5, 10.1]
+  ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_,
+  ## but modifies the sequence directly.
+  ##
+  ## See also:
+  ## * `keepItIf template<#keepItIf.t,seq,untyped>`_
+  ## * `filter proc<#filter,openArray[T],proc(T)>`_
+  ##
+  runnableExamples:
+    var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
+    keepIf(floats, proc(x: float): bool = x > 10)
+    assert floats == @[13.0, 12.5, 10.1]
+
   var pos = 0
   for i in 0 ..< len(s):
     if pred(s[i]):
@@ -360,16 +421,15 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
   setLen(s, pos)
 
 proc delete*[T](s: var seq[T]; first, last: Natural) =
-  ## Deletes in `s` the items at position `first` .. `last`. This modifies
-  ## `s` itself, it does not return a copy.
-  ##
-  ## Example:
+  ## Deletes in the items of a sequence `s` at positions ``first..last``
+  ## (including both ends of a range).
+  ## This modifies `s` itself, it does not return a copy.
   ##
-  ##.. code-block::
-  ##   let outcome = @[1,1,1,1,1,1,1,1]
-  ##   var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-  ##   dest.delete(3, 8)
-  ##   assert outcome == dest
+  runnableExamples:
+    let outcome = @[1,1,1,1,1,1,1,1]
+    var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    dest.delete(3, 8)
+    assert outcome == dest
 
   var i = first
   var j = last+1
@@ -384,15 +444,15 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
   ## Inserts items from `src` into `dest` at position `pos`. This modifies
   ## `dest` itself, it does not return a copy.
   ##
-  ## Example:
+  ## Notice that `src` and `dest` must be of the same type.
   ##
-  ##.. code-block::
-  ##   var dest = @[1,1,1,1,1,1,1,1]
-  ##   let
-  ##     src = @[2,2,2,2,2,2]
-  ##     outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
-  ##   dest.insert(src, 3)
-  ##   assert dest == outcome
+  runnableExamples:
+    var dest = @[1,1,1,1,1,1,1,1]
+    let
+      src = @[2,2,2,2,2,2]
+      outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
+    dest.insert(src, 3)
+    assert dest == outcome
 
   var j = len(dest) - 1
   var i = len(dest) + len(src) - 1
@@ -411,37 +471,48 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
 
 
 template filterIt*(s, pred: untyped): untyped =
-  ## Returns a new sequence with all the items that fulfilled the predicate.
+  ## Returns a new sequence with all the items of `s` that fulfilled the
+  ## predicate `pred`.
   ##
-  ## Unlike the `proc` version, the predicate needs to be an expression using
-  ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
+  ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and
+  ## `filter iterator<#filter.i,openArray[T],proc(T)>`_,
+  ## the predicate needs to be an expression using the ``it`` variable
+  ## for testing, like: ``filterIt("abcxyz", it == 'x')``.
   ##
-  ## Example:
+  ## See also:
+  ## * `fliter proc<#filter,openArray[T],proc(T)>`_
+  ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##    let
-  ##      temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
-  ##      acceptable = filterIt(temperatures, it < 50 and it > -10)
-  ##      notAcceptable = filterIt(temperatures, it > 50 or it < -10)
-  ##    assert acceptable == @[-2.0, 24.5, 44.31]
-  ##    assert notAcceptable == @[-272.15, 99.9, -113.44]
+  runnableExamples:
+    let
+      temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
+      acceptable = temperatures.filterIt(it < 50 and it > -10)
+      notAcceptable = temperatures.filterIt(it > 50 or it < -10)
+    assert acceptable == @[-2.0, 24.5, 44.31]
+    assert notAcceptable == @[-272.15, 99.9, -113.44]
+
   var result = newSeq[type(s[0])]()
   for it {.inject.} in items(s):
     if pred: result.add(it)
   result
 
 template keepItIf*(varSeq: seq, pred: untyped) =
-  ## Convenience template around the ``keepIf`` proc to reduce typing.
+  ## Keeps the items in the passed sequence (must be declared as a ``var``)
+  ## if they fulfilled the predicate.
   ##
-  ## Unlike the `proc` version, the predicate needs to be an expression using
+  ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_,
+  ## the predicate needs to be an expression using
   ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
   ##
-  ## Example:
+  ## See also:
+  ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_
+  ## * `filterIt template<#filterIt.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   var candidates = @["foo", "bar", "baz", "foobar"]
-  ##   keepItIf(candidates, it.len == 3 and it[0] == 'b')
-  ##   assert candidates == @["bar", "baz"]
+  runnableExamples:
+    var candidates = @["foo", "bar", "baz", "foobar"]
+    candidates.keepItIf(it.len == 3 and it[0] == 'b')
+    assert candidates == @["bar", "baz"]
+
   var pos = 0
   for i in 0 ..< len(varSeq):
     let it {.inject.} = varSeq[i]
@@ -455,26 +526,37 @@ proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
   ## Iterates through a container and checks if every item fulfills the
   ## predicate.
   ##
-  ## Example:
+  ## See also:
+  ## * `allIt template<#allIt.t,untyped,untyped>`_
+  ## * `any proc<#any,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert all(numbers, proc (x: int): bool = return x < 10) == true
-  ##   assert all(numbers, proc (x: int): bool = return x < 9) == false
+  runnableExamples:
+     let numbers = @[1, 4, 5, 8, 9, 7, 4]
+     assert all(numbers, proc (x: int): bool = return x < 10) == true
+     assert all(numbers, proc (x: int): bool = return x < 9) == false
+
   for i in s:
     if not pred(i):
       return false
   return true
 
 template allIt*(s, pred: untyped): bool =
-  ## Checks if every item fulfills the predicate.
+  ## Iterates through a container and checks if every item fulfills the
+  ## predicate.
   ##
-  ## Example:
+  ## Unlike the `all proc<#all,openArray[T],proc(T)>`_,
+  ## the predicate needs to be an expression using
+  ## the ``it`` variable for testing, like: ``allIt("abba", it == 'a')``.
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert allIt(numbers, it < 10) == true
-  ##   assert allIt(numbers, it < 9) == false
+  ## See also:
+  ## * `all proc<#all,openArray[T],proc(T)>`_
+  ## * `anyIt template<#anyIt.t,untyped,untyped>`_
+  ##
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    assert numbers.allIt(it < 10) == true
+    assert numbers.allIt(it < 9) == false
+
   var result = true
   for it {.inject.} in items(s):
     if not pred:
@@ -486,26 +568,37 @@ proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
   ## Iterates through a container and checks if some item fulfills the
   ## predicate.
   ##
-  ## Example:
+  ## See also:
+  ## * `anyIt template<#anyIt.t,untyped,untyped>`_
+  ## * `all proc<#all,openArray[T],proc(T)>`_
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert any(numbers, proc (x: int): bool = return x > 8) == true
-  ##   assert any(numbers, proc (x: int): bool = return x > 9) == false
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    assert any(numbers, proc (x: int): bool = return x > 8) == true
+    assert any(numbers, proc (x: int): bool = return x > 9) == false
+
   for i in s:
     if pred(i):
       return true
   return false
 
 template anyIt*(s, pred: untyped): bool =
-  ## Checks if some item fulfills the predicate.
+  ## Iterates through a container and checks if some item fulfills the
+  ## predicate.
   ##
-  ## Example:
+  ## Unlike the `any proc<#any,openArray[T],proc(T)>`_,
+  ## the predicate needs to be an expression using
+  ## the ``it`` variable for testing, like: ``anyIt("abba", it == 'a')``.
   ##
-  ## .. code-block::
-  ##   let numbers = @[1, 4, 5, 8, 9, 7, 4]
-  ##   assert anyIt(numbers, it > 8) == true
-  ##   assert anyIt(numbers, it > 9) == false
+  ## See also:
+  ## * `any proc<#any,openArray[T],proc(T)>`_
+  ## * `allIt template<#allIt.t,untyped,untyped>`_
+  ##
+  runnableExamples:
+    let numbers = @[1, 4, 5, 8, 9, 7, 4]
+    assert numbers.anyIt(it > 8) == true
+    assert numbers.anyIt(it > 9) == false
+
   var result = false
   for it {.inject.} in items(s):
     if pred:
@@ -555,19 +648,28 @@ template toSeq2(iter: iterator): untyped =
     result
 
 template toSeq*(iter: untyped): untyped =
-  ## Transforms any iterable into a sequence.
+  ## Transforms any iterable (anything that can be iterated over, e.g. with
+  ## a for-loop) into a sequence.
+  ##
   runnableExamples:
     let
-      numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
-      odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1))
-    doAssert odd_numbers == @[1, 3, 5, 7, 9]
+      myRange = 1..5
+      mySet: set[int8] = {5'i8, 3, 1}
+    assert type(myRange) is HSlice[system.int, system.int]
+    assert type(mySet) is set[int8]
+
+    let
+      mySeq1 = toSeq(myRange)
+      mySeq2 = toSeq(mySet)
+    assert mySeq1 == @[1, 2, 3, 4, 5]
+    assert mySeq2 == @[1'i8, 3, 5]
 
   when compiles(toSeq1(iter)):
     toSeq1(iter)
   elif compiles(toSeq2(iter)):
     toSeq2(iter)
   else:
-    # overload for untyped, eg: `toSeq(myInlineIterator(3))`
+    # overload for untyped, e.g.: `toSeq(myInlineIterator(3))`
     when compiles(iter.len):
       block:
         evalOnceAs(iter2, iter, true)
@@ -597,20 +699,23 @@ template foldl*(sequence, operation: untyped): untyped =
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
   ## 3).
   ##
-  ## Example:
+  ## See also:
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
+  ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[5, 9, 11]
-  ##     addition = foldl(numbers, a + b)
-  ##     subtraction = foldl(numbers, a - b)
-  ##     multiplication = foldl(numbers, a * b)
-  ##     words = @["nim", "is", "cool"]
-  ##     concatenation = foldl(words, a & b)
-  ##   assert addition == 25, "Addition is (((5)+9)+11)"
-  ##   assert subtraction == -15, "Subtraction is (((5)-9)-11)"
-  ##   assert multiplication == 495, "Multiplication is (((5)*9)*11)"
-  ##   assert concatenation == "nimiscool"
+  runnableExamples:
+    let
+      numbers = @[5, 9, 11]
+      addition = foldl(numbers, a + b)
+      subtraction = foldl(numbers, a - b)
+      multiplication = foldl(numbers, a * b)
+      words = @["nim", "is", "cool"]
+      concatenation = foldl(words, a & b)
+    assert addition == 25, "Addition is (((5)+9)+11)"
+    assert subtraction == -15, "Subtraction is (((5)-9)-11)"
+    assert multiplication == 495, "Multiplication is (((5)*9)*11)"
+    assert concatenation == "nimiscool"
+
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
   var result: type(s[0])
@@ -625,20 +730,22 @@ template foldl*(sequence, operation: untyped): untyped =
 template foldl*(sequence, operation, first): untyped =
   ## Template to fold a sequence from left to right, returning the accumulation.
   ##
-  ## This version of ``foldl`` gets a starting parameter. This makes it possible
+  ## This version of ``foldl`` gets a **starting parameter**. This makes it possible
   ## to accumulate the sequence into a different type than the sequence elements.
   ##
   ## The ``operation`` parameter should be an expression which uses the variables
   ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the
   ## start value (the first ``a``) and therefor defines the type of the result.
   ##
-  ## Example:
+  ## See also:
+  ## * `foldr template<#foldr.t,untyped,untyped>`_
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[0, 8, 1, 5]
-  ##     digits = foldl(numbers, a & (chr(b + ord('0'))), "")
-  ##   assert digits == "0815"
+  runnableExamples:
+    let
+      numbers = @[0, 8, 1, 5]
+      digits = foldl(numbers, a & (chr(b + ord('0'))), "")
+    assert digits == "0815"
+
   var result: type(first)
   result = first
   for x in items(sequence):
@@ -662,20 +769,23 @@ template foldr*(sequence, operation: untyped): untyped =
   ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
   ## (3))).
   ##
-  ## Example:
+  ## See also:
+  ## * `foldl template<#foldl.t,untyped,untyped>`_
+  ## * `foldl template<#foldl.t,,,>`_ with a starting parameter
   ##
-  ## .. code-block::
-  ##   let
-  ##     numbers = @[5, 9, 11]
-  ##     addition = foldr(numbers, a + b)
-  ##     subtraction = foldr(numbers, a - b)
-  ##     multiplication = foldr(numbers, a * b)
-  ##     words = @["nim", "is", "cool"]
-  ##     concatenation = foldr(words, a & b)
-  ##   assert addition == 25, "Addition is (5+(9+(11)))"
-  ##   assert subtraction == 7, "Subtraction is (5-(9-(11)))"
-  ##   assert multiplication == 495, "Multiplication is (5*(9*(11)))"
-  ##   assert concatenation == "nimiscool"
+  runnableExamples:
+    let
+      numbers = @[5, 9, 11]
+      addition = foldr(numbers, a + b)
+      subtraction = foldr(numbers, a - b)
+      multiplication = foldr(numbers, a * b)
+      words = @["nim", "is", "cool"]
+      concatenation = foldr(words, a & b)
+    assert addition == 25, "Addition is (5+(9+(11)))"
+    assert subtraction == 7, "Subtraction is (5-(9-(11)))"
+    assert multiplication == 495, "Multiplication is (5*(9*(11)))"
+    assert concatenation == "nimiscool"
+
   let s = sequence
   assert s.len > 0, "Can't fold empty sequences"
   var result: type(s[0])
@@ -687,41 +797,26 @@ template foldr*(sequence, operation: untyped): untyped =
     result = operation
   result
 
-template mapIt*(s, typ, op: untyped): untyped =
-  ## Convenience template around the ``map`` proc to reduce typing.
-  ##
-  ## The template injects the ``it`` variable which you can use directly in an
-  ## expression. You also need to pass as `typ` the type of the expression,
-  ## since the new returned sequence can have a different type than the
-  ## original.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##   let
-  ##     nums = @[1, 2, 3, 4]
-  ##     strings = nums.mapIt(string, $(4 * it))
-  ##   assert strings == @["4", "8", "12", "16"]
-  ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
-  ##   template instead.
-  var result: seq[typ] = @[]
-  for it {.inject.} in items(s):
-    result.add(op)
-  result
-
 template mapIt*(s: typed, op: untyped): untyped =
-  ## Convenience template around the ``map`` proc to reduce typing.
+  ## Returns a new sequence with the results of `op` proc applied to every
+  ## item in the container `s`.
+  ##
+  ## Since the input is not modified you can use it to
+  ## transform the type of the elements in the input container.
   ##
   ## The template injects the ``it`` variable which you can use directly in an
   ## expression.
   ##
-  ## Example:
+  ## See also:
+  ## * `map proc<#map,openArray[T],proc(T)>`_
+  ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version
   ##
-  ## .. code-block::
-  ##   let
-  ##     nums = @[1, 2, 3, 4]
-  ##     strings = nums.mapIt($(4 * it))
-  ##   assert strings == @["4", "8", "12", "16"]
+  runnableExamples:
+    let
+      nums = @[1, 2, 3, 4]
+      strings = nums.mapIt($(4 * it))
+    assert strings == @["4", "8", "12", "16"]
+
   when defined(nimHasTypeof):
     type outType = typeof((
       block:
@@ -758,31 +853,38 @@ template applyIt*(varSeq, op: untyped) =
   ## expression. The expression has to return the same type as the sequence you
   ## are mutating.
   ##
-  ## Example:
+  ## See also:
+  ## * `apply proc<#apply,openArray[T],proc(T)_2>`_
+  ## * `mapIt template<#mapIt.t,typed,untyped>`_
   ##
-  ## .. code-block::
-  ##   var nums = @[1, 2, 3, 4]
-  ##   nums.applyIt(it * 3)
-  ##   assert nums[0] + nums[3] == 15
+  runnableExamples:
+     var nums = @[1, 2, 3, 4]
+     nums.applyIt(it * 3)
+     assert nums[0] + nums[3] == 15
+
   for i in low(varSeq) .. high(varSeq):
     let it {.inject.} = varSeq[i]
     varSeq[i] = op
 
 
 template newSeqWith*(len: int, init: untyped): untyped =
-  ## creates a new sequence, calling `init` to initialize each value.
+  ## Creates a new sequence of length `len`, calling `init` to initialize
+  ## each value of the sequence.
   ##
-  ## Example:
+  ## Useful for creating "2D" sequences - sequences containing other sequences
+  ## or to populate fields of the created sequence.
   ##
-  ## .. code-block::
-  ##   var seq2D = newSeqWith(20, newSeq[bool](10))
-  ##   seq2D[0][0] = true
-  ##   seq2D[1][0] = true
-  ##   seq2D[0][1] = true
-  ##
-  ##   import random
-  ##   var seqRand = newSeqWith(20, random(10))
-  ##   echo seqRand
+  runnableExamples:
+    ## Creates a seqence containing 5 bool sequences, each of length of 3.
+    var seq2D = newSeqWith(5, newSeq[bool](3))
+    assert seq2D.len == 5
+    assert seq2D[0].len == 3
+    assert seq2D[4][2] == false
+
+    ## Creates a sequence of 20 random numbers from 1 to 10
+    import random
+    var seqRand = newSeqWith(20, random(10))
+
   var result = newSeq[type(init)](len)
   for i in 0 ..< len:
     result[i] = init
@@ -804,7 +906,7 @@ proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool;
 
 macro mapLiterals*(constructor, op: untyped;
                    nested = true): untyped =
-  ## applies ``op`` to each of the **atomic** literals like ``3``
+  ## Applies ``op`` to each of the **atomic** literals like ``3``
   ## or ``"abc"`` in the specified ``constructor`` AST. This can
   ## be used to map every array element to some target type:
   ##
@@ -819,16 +921,20 @@ macro mapLiterals*(constructor, op: untyped;
   ## .. code-block::
   ##   let x = [int(0.1), int(1.2), int(2.3), int(3.4)]
   ##
-  ## If ``nested`` is true, the literals are replaced everywhere
-  ## in the ``constructor`` AST, otherwise only the first level
+  ## If ``nested`` is true (which is the default), the literals are replaced
+  ## everywhere in the ``constructor`` AST, otherwise only the first level
   ## is considered:
   ##
   ## .. code-block::
-  ##   mapLiterals((1, ("abc"), 2), float, nested=false)
-  ##
-  ## Produces::
-  ##
-  ##   (float(1), ("abc"), float(2))
+  ##   let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int)
+  ##   let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false)
+  ##   assert a == (1, (2, 3), 4)
+  ##   assert b == (1, (2.3, 3.4), 4)
+  ##
+  ##   let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`)
+  ##   let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false)
+  ##   assert c == ("1", ("2", "3"), "4", ("5", "6"))
+  ##   assert d == ("1", (2, 3), "4", (5, 6))
   ##
   ## There are no constraints for the ``constructor`` AST, it
   ## works for nested tuples of arrays of sets etc.
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 1273cbc33..d1f941e92 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -191,13 +191,6 @@ proc `[]`*[A](s: var HashSet[A], key: A): var A =
     else:
       raise newException(KeyError, "key not found")
 
-proc mget*[A](s: var HashSet[A], key: A): var A {.deprecated.} =
-  ## returns the element that is actually stored in 's' which has the same
-  ## value as 'key' or raises the ``KeyError`` exception. This is useful
-  ## when one overloaded 'hash' and '==' but still needs reference semantics
-  ## for sharing. Use ```[]``` instead.
-  s[key]
-
 proc contains*[A](s: HashSet[A], key: A): bool =
   ## Returns true iff `key` is in `s`.
   ##
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 9a5bffcef..2cdc62996 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## An ``include`` file for the different table implementations.
+# An ``include`` file for the different table implementations.
 
 # hcode for real keys cannot be zero.  hcode==0 signifies an empty slot.  These
 # two procs retain clarity of that encoding without the space cost of an enum.
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index f46a368b1..84ec422d4 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -9,16 +9,21 @@
 
 ## The ``tables`` module implements variants of an efficient `hash table`:idx:
 ## (also often named `dictionary`:idx: in other programming languages) that is
-## a mapping from keys to values. ``Table`` is the usual hash table,
-## ``OrderedTable`` is like ``Table`` but remembers insertion order
-## and ``CountTable`` is a mapping from a key to its number of occurrences.
+## a mapping from keys to values.
+##
+## There are several different types of hash tables available:
+## * `Table<#Table>`_ is the usual hash table,
+## * `OrderedTable<#OrderedTable>`_ is like ``Table`` but remembers insertion order,
+## * `CountTable<#CountTable>`_ is a mapping from a key to its number of occurrences
 ##
 ## For consistency with every other data type in Nim these have **value**
 ## semantics, this means that ``=`` performs a copy of the hash table.
-## For **reference** semantics use the ``Ref`` variant: ``TableRef``,
-## ``OrderedTableRef``, ``CountTableRef``.
 ##
-## To give an example, when ``a`` is a Table, then ``var b = a`` gives ``b``
+## For `ref semantics<manual.html#types-ref-and-pointer-types>`_
+## use their ``Ref`` variants: `TableRef<#TableRef>`_,
+## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
+##
+## To give an example, when ``a`` is a ``Table``, then ``var b = a`` gives ``b``
 ## as a new independent table. ``b`` is initialised with the contents of ``a``.
 ## Changing ``b`` does not affect ``a`` and vice versa:
 ##
@@ -35,8 +40,8 @@
 ##   echo a, b  # output: {1: one, 2: two}{1: one, 2: two, 3: three}
 ##   echo a == b  # output: false
 ##
-## On the other hand, when ``a`` is a TableRef instead, then changes to ``b``
-## also affect ``a``. Both ``a`` and ``b`` reference the same data structure:
+## On the other hand, when ``a`` is a ``TableRef`` instead, then changes to ``b``
+## also affect ``a``. Both ``a`` and ``b`` **ref** the same data structure:
 ##
 ## .. code-block::
 ##   import tables
@@ -51,27 +56,111 @@
 ##   echo a, b  # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three}
 ##   echo a == b  # output: true
 ##
+## ----
+##
+## Basic usage
+## ===========
+##
+## Table
+## -----
+##
+## .. code-block::
+##   import tables
+##   from sequtils import zip
+##
+##   let
+##     names = ["John", "Paul", "George", "Ringo"]
+##     years = [1940, 1942, 1943, 1940]
+##
+##   var beatles = initTable[string, int]()
+##
+##   for pairs in zip(names, years):
+##     let (name, birthYear) = pairs
+##     beatles[name] = birthYear
+##
+##   echo beatles
+##   # {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}
+##
+##
+##   var beatlesByYear = initTable[int, seq[string]]()
+##
+##   for pairs in zip(years, names):
+##     let (birthYear, name) = pairs
+##     if not beatlesByYear.hasKey(birthYear):
+##       # if a key doesn't exists, we create one with an empty sequence
+##       # before we can add elements to it
+##       beatlesByYear[birthYear] = @[]
+##     beatlesByYear[birthYear].add(name)
+##
+##   echo beatlesByYear
+##   # {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}
+##
+##
 ##
-## Here is an example of ``CountTable`` usage:
+## OrderedTable
+## ------------
+##
+## `OrderedTable<#OrderedTable>`_ is used when it is important to preserve
+## the insertion order of keys.
+##
+## .. code-block::
+##   import tables
+##
+##   let
+##     a = [('z', 1), ('y', 2), ('x', 3)]
+##     t = a.toTable          # regular table
+##     ot = a.toOrderedTable  # ordered tables
+##
+##   echo t   # {'x': 3, 'y': 2, 'z': 1}
+##   echo ot  # {'z': 1, 'y': 2, 'x': 3}
+##
+##
+##
+## CountTable
+## ----------
+##
+## `CountTable<#CountTable>`_ is useful for counting number of items of some
+## container (e.g. string, sequence or array), as it is a mapping where the
+## items are the keys, and their number of occurrences are the values.
+## For that purpose `toCountTable proc<#toCountTable,openArray[A]>`_
+## comes handy:
+##
+## .. code-block::
+##   import tables
 ##
-## .. code-block:: nim
 ##   let myString = "abracadabra"
-##   var myTable = initCountTable[char]()
+##   let letterFrequencies = toCountTable(myString)
+##   echo letterFrequencies
+##   # 'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
+##
+## The same could have been achieved by manually iterating over a container
+## and increasing each key's value with `inc proc<#inc,CountTable[A],A,int>`_:
+##
+## .. code-block::
+##   import tables
 ##
+##   let myString = "abracadabra"
+##   var letterFrequencies = initCountTable[char]()
 ##   for c in myString:
-##     myTable.inc(c)
+##     letterFrequencies.inc(c)
+##   echo letterFrequencies
+##   # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
+##
+## ----
+##
 ##
-##   echo myTable  # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
 ##
+## Hashing
+## -------
 ##
 ## If you are using simple standard types like ``int`` or ``string`` for the
 ## keys of the table you won't have any problems, but as soon as you try to use
 ## a more complex object as a key you will be greeted by a strange compiler
-## error::
+## error:
 ##
 ##   Error: type mismatch: got (Person)
 ##   but expected one of:
-##   hashes.hash(x: openarray[A]): Hash
+##   hashes.hash(x: openArray[A]): Hash
 ##   hashes.hash(x: int): Hash
 ##   hashes.hash(x: float): Hash
 ##   …
@@ -89,6 +178,8 @@
 ## example implementing only ``hash`` suffices:
 ##
 ## .. code-block::
+##   import tables, hashes
+##
 ##   type
 ##     Person = object
 ##       firstName, lastName: string
@@ -111,45 +202,50 @@
 ##   p2.firstName = "소진"
 ##   p2.lastName = "박"
 ##   salaries[p2] = 45_000
+##
+## ----
+##
+## See also
+## ========
+##
+## * `json module<json.html>`_ for table-like structure which allows
+##   heterogeneous members
+## * `sharedtables module<sharedtables.html>`_ for shared hash table support
+## * `strtabs module<strtabs.html>`_ for efficient hash tables
+##   mapping from strings to strings
+## * `hashes module<hashes.html>`_ for helper functions for hashing
 
-import
-  hashes, math
+
+import hashes, math
 
 include "system/inclrtl"
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
   KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
-  Table*[A, B] = object ## generic hash table
+  Table*[A, B] = object
+    ## Generic hash table, consisting of a key-value pair.
+    ##
+    ## `data` and `counter` are internal implementation details which
+    ## can't be accessed.
+    ##
+    ## For creating an empty Table, use `initTable proc<#initTable,int>`_.
     data: KeyValuePairSeq[A, B]
     counter: int
-  TableRef*[A,B] = ref Table[A, B]
+  TableRef*[A,B] = ref Table[A, B] ## Ref version of `Table<#Table>`_.
+    ##
+    ## For creating a new empty TableRef, use `newTable proc
+    ## <#newTable,int>`_.
+
+
+# ------------------------------ helpers ---------------------------------
 
 template maxHash(t): untyped = high(t.data)
 template dataLen(t): untyped = len(t.data)
 
 include tableimpl
 
-proc clear*[A, B](t: var Table[A, B]) =
-  ## resets the table so that it is empty.
-  clearImpl()
-
-proc clear*[A, B](t: TableRef[A, B]) =
-  ## resets the ref table so that it is empty.
-  clearImpl()
-
-proc rightSize*(count: Natural): int {.inline.} =
-  ## return the value of ``initialSize`` to support ``count`` items.
-  ##
-  ## If more items are expected to be added, simply add that
-  ## expected extra amount to the parameter before calling this.
-  ##
-  ## Internally, we want mustRehash(rightSize(x), x) == false.
-  result = nextPowerOfTwo(count * 3 div 2  +  4)
-
-proc len*[A, B](t: Table[A, B]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc rightSize*(count: Natural): int {.inline.}
 
 template get(t, key): untyped =
   ## retrieves the value at ``t[key]``. The value can be modified.
@@ -176,36 +272,340 @@ template getOrDefaultImpl(t, key, default: untyped): untyped =
   var index = rawGet(t, key, hc)
   result = if index >= 0: t.data[index].val else: default
 
-proc `[]`*[A, B](t: Table[A, B], key: A): B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  get(t, key)
+template dollarImpl(): untyped {.dirty.} =
+  if t.len == 0:
+    result = "{:}"
+  else:
+    result = "{"
+    for key, val in pairs(t):
+      if result.len > 1: result.add(", ")
+      result.addQuoted(key)
+      result.add(": ")
+      result.addQuoted(val)
+    result.add("}")
 
-proc `[]`*[A, B](t: var Table[A, B], key: A): var B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
+proc enlarge[A, B](t: var Table[A, B]) =
+  var n: KeyValuePairSeq[A, B]
+  newSeq(n, len(t.data) * growthFactor)
+  swap(t.data, n)
+  for i in countup(0, high(n)):
+    let eh = n[i].hcode
+    if isFilled(eh):
+      var j: Hash = eh and maxHash(t)
+      while isFilled(t.data[j].hcode):
+        j = nextTry(j, maxHash(t))
+      rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
+
+template equalsImpl(s, t: typed): typed =
+  if s.counter == t.counter:
+    # different insertion orders mean different 'data' seqs, so we have
+    # to use the slow route here:
+    for key, val in s:
+      if not t.hasKey(key): return false
+      if t.getOrDefault(key) != val: return false
+    return true
+
+
+
+# -------------------------------------------------------------------
+# ------------------------------ Table ------------------------------
+# -------------------------------------------------------------------
+
+proc initTable*[A, B](initialSize=64): Table[A, B] =
+  ## Creates a new hash table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
+  ##
+  ## See also:
+  ## * `toTable proc<#toTable,openArray[]>`_
+  ## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
+  runnableExamples:
+    let
+      a = initTable[int, string]()
+      b = initTable[char, seq[int]]()
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  newSeq(result.data, initialSize)
+
+proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
+  ## Creates a new hash table that contains the given ``pairs``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `initTable proc<#initTable,int>`_
+  ## * `newTable proc<#newTable,openArray[]>`_ for a `TableRef` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = toTable(a)
+    assert b == {'a': 5, 'b': 9}.toTable
+  result = initTable[A, B](rightSize(pairs.len))
+  for key, val in items(pairs): result[key] = val
+
+proc `[]`*[A, B](t: Table[A, B], key: A): B =
+  ## Retrieves the value at ``t[key]``.
+  ##
   ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ## One can check with `hasKey proc<#hasKey,Table[A,B],A>`_ whether
+  ## the key exists.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in
+  ##   the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
   get(t, key)
 
-proc mget*[A, B](t: var Table[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
+proc `[]`*[A, B](t: var Table[A, B], key: A): var B =
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
   ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in
+  ##   the table
   get(t, key)
 
+proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `del proc<#del,Table[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = initTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.toTable
+  putImpl(enlarge)
+
+proc hasKey*[A, B](t: Table[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,Table[A,B],A>`_ for use with the `in` operator
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  var hc: Hash
+  result = rawGet(t, key, hc) >= 0
+
+proc contains*[A, B](t: Table[A, B], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,Table[A,B],A>`_ for use with
+  ## the ``in`` operator.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  return hasKey[A, B](t, key)
+
+proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
+  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
+  hasKeyOrPutImpl(enlarge)
+
 proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
   ## default initialization value for type ``B`` is returned (e.g. 0 for any
   ## integer type).
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``.
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
   ## Otherwise, ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,Table[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   getOrDefaultImpl(t, key, default)
 
+proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
+  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,Table[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,Table[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
+  mgetOrPutImpl(enlarge)
+
+proc len*[A, B](t: Table[A, B]): int =
+  ## Returns the number of keys in ``t``.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toTable
+    doAssert len(a) == 2
+  result = t.counter
+
+proc add*[A, B](t: var Table[A, B], key: A, val: B) =
+  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
+  addImpl(enlarge)
+
+proc del*[A, B](t: var Table[A, B], key: A) =
+  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ##
+  ## See also:
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.toTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.toTable
+  delImpl()
+
+proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
+  ## Deletes the ``key`` from the table.
+  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
+  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## unchanged.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'a': 5, 'b': 9, 'c': 13}.toTable
+      i: int
+    doAssert a.take('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.toTable
+    doAssert i == 9
+    i = 0
+    doAssert a.take('z', i) == false
+    doAssert a == {'a': 5, 'c': 13}.toTable
+    doAssert i == 0
+
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = index >= 0
+  if result:
+    shallowCopy(val, t.data[index].val)
+    delImplIdx(t, index)
+
+proc clear*[A, B](t: var Table[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
+
+proc `$`*[A, B](t: Table[A, B]): string =
+  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## on a table.
+  dollarImpl()
+
+proc `==`*[A, B](s, t: Table[A, B]): bool =
+  ## The ``==`` operator for hash tables. Returns ``true`` if the content of both
+  ## tables contains the same key-value pairs. Insert order does not matter.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.toTable
+      b = {'b': 9, 'c': 13, 'a': 5}.toTable
+    doAssert a == b
+  equalsImpl(s, t)
+
+proc rightSize*(count: Natural): int {.inline.} =
+  ## Return the value of ``initialSize`` to support ``count`` items.
+  ##
+  ## If more items are expected to be added, simply add that
+  ## expected extra amount to the parameter before calling this.
+  ##
+  ## Internally, we want mustRehash(rightSize(x), x) == false.
+  result = nextPowerOfTwo(count * 3 div 2  +  4)
+
+proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
+  ## Index the collection with the proc provided.
+  # TODO: As soon as supported, change collection: A to collection: A[B]
+  result = initTable[C, B]()
+  for item in collection:
+    result[index(item)] = item
+
+
+
 template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
-  ## retrieves the value at ``t[key]``.
+  ## Retrieves the value at ``t[key]``.
+  ##
   ## ``value`` can be modified in the scope of the ``withValue`` call.
   ##
   ## .. code-block:: nim
@@ -225,7 +625,8 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
 
 template withValue*[A, B](t: var Table[A, B], key: A,
                           value, body1, body2: untyped) =
-  ## retrieves the value at ``t[key]``.
+  ## Retrieves the value at ``t[key]``.
+  ##
   ## ``value`` can be modified in the scope of the ``withValue`` call.
   ##
   ## .. code-block:: nim
@@ -248,370 +649,535 @@ template withValue*[A, B](t: var Table[A, B], key: A,
   else:
     body2
 
-iterator allValues*[A, B](t: Table[A, B]; key: A): B =
-  ## iterates over any value in the table ``t`` that belongs to the given ``key``.
-  var h: Hash = genHash(key) and high(t.data)
-  while isFilled(t.data[h].hcode):
-    if t.data[h].key == key:
-      yield t.data[h].val
-    h = nextTry(h, high(t.data))
-
-proc hasKey*[A, B](t: Table[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  var hc: Hash
-  result = rawGet(t, key, hc) >= 0
-
-proc contains*[A, B](t: Table[A, B], key: A): bool =
-  ## alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
 
 iterator pairs*[A, B](t: Table[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_
+  ## * `keys iterator<#keys.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.toTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
 iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values
-  ## can be modified.
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
 iterator keys*[A, B](t: Table[A, B]): A =
-  ## iterates over any key in the table ``t``.
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].key
 
 iterator values*[A, B](t: Table[A, B]): B =
-  ## iterates over any value in the table ``t``.
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,Table[A,B]>`_
+  ## * `keys iterator<#keys.i,Table[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,Table[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for v in a.values:
+      doAssert v.len == 4
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
 iterator mvalues*[A, B](t: var Table[A, B]): var B =
-  ## iterates over any value in the table ``t``. The values can be modified.
+  ## Iterates over any value in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_
+  ## * `values iterator<#values.i,Table[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
-proc del*[A, B](t: var Table[A, B], key: A) =
-  ## deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
-  delImpl()
-
-proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
-  ## deletes the ``key`` from the table.
-  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
-  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
-  ## unchanged.
-  var hc: Hash
-  var index = rawGet(t, key, hc)
-  result = index >= 0
-  if result:
-    shallowCopy(val, t.data[index].val)
-    delImplIdx(t, index)
-
-proc enlarge[A, B](t: var Table[A, B]) =
-  var n: KeyValuePairSeq[A, B]
-  newSeq(n, len(t.data) * growthFactor)
-  swap(t.data, n)
-  for i in countup(0, high(n)):
-    let eh = n[i].hcode
-    if isFilled(eh):
-      var j: Hash = eh and maxHash(t)
-      while isFilled(t.data[j].hcode):
-        j = nextTry(j, maxHash(t))
-      rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
-
-proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
-  ## returning a value which can be modified.
-  mgetOrPutImpl(enlarge)
-
-proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  hasKeyOrPutImpl(enlarge)
-
-proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  putImpl(enlarge)
-
-proc add*[A, B](t: var Table[A, B], key: A, val: B) =
-  ## puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
-  ## This can introduce duplicate keys into the table!
-  addImpl(enlarge)
-
-proc len*[A, B](t: TableRef[A, B]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
-
-proc initTable*[A, B](initialSize=64): Table[A, B] =
-  ## creates a new hash table that is empty.
+iterator allValues*[A, B](t: Table[A, B]; key: A): B =
+  ## Iterates over any value in the table ``t`` that belongs to the given ``key``.
   ##
-  ## ``initialSize`` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
+  ## Used if you have a table with duplicate keys (as a result of using
+  ## `add proc<#add,Table[A,B],A,B>`_).
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   var a = {'a': 3, 'b': 5}.toTable
+  ##   for i in 1..3:
+  ##     a.add('z', 10*i)
+  ##   echo a # {'a': 3, 'b': 5, 'z': 10, 'z': 20, 'z': 30}
+  ##
+  ##   for v in a.allValues('z'):
+  ##     echo v
+  ##   # 10
+  ##   # 20
+  ##   # 30
+  var h: Hash = genHash(key) and high(t.data)
+  while isFilled(t.data[h].hcode):
+    if t.data[h].key == key:
+      yield t.data[h].val
+    h = nextTry(h, high(t.data))
 
-proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
-  ## creates a new hash table that contains the given ``pairs``.
-  result = initTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result[key] = val
 
-template dollarImpl(): untyped {.dirty.} =
-  if t.len == 0:
-    result = "{:}"
-  else:
-    result = "{"
-    for key, val in pairs(t):
-      if result.len > 1: result.add(", ")
-      result.addQuoted(key)
-      result.add(": ")
-      result.addQuoted(val)
-    result.add("}")
 
-proc `$`*[A, B](t: Table[A, B]): string =
-  ## the ``$`` operator for hash tables.
-  dollarImpl()
+# -------------------------------------------------------------------
+# ---------------------------- TableRef -----------------------------
+# -------------------------------------------------------------------
 
-proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
 
-template equalsImpl(s, t: typed): typed =
-  if s.counter == t.counter:
-    # different insertion orders mean different 'data' seqs, so we have
-    # to use the slow route here:
-    for key, val in s:
-      if not t.hasKey(key): return false
-      if t.getOrDefault(key) != val: return false
-    return true
+proc newTable*[A, B](initialSize=64): TableRef[A, B] =
+  ## Creates a new ref hash table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
+  ##
+  ## See also:
+  ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef`
+  ##   from a collection of `(key, value)` pairs
+  ## * `initTable proc<#initTable,int>`_ for creating a `Table`
+  runnableExamples:
+    let
+      a = newTable[int, string]()
+      b = newTable[char, seq[int]]()
+  new(result)
+  result[] = initTable[A, B](initialSize)
 
-proc `==`*[A, B](s, t: Table[A, B]): bool =
-  ## The ``==`` operator for hash tables. Returns ``true`` iff the content of both
-  ## tables contains the same key-value pairs. Insert order does not matter.
-  equalsImpl(s, t)
+proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
+  ## Creates a new ref hash table that contains the given ``pairs``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `newTable proc<#newTable,int>`_
+  ## * `toTable proc<#toTable,openArray[]>`_ for a `Table` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = newTable(a)
+    assert b == {'a': 5, 'b': 9}.newTable
+  new(result)
+  result[] = toTable[A, B](pairs)
 
-proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] =
+proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
   ## Index the collection with the proc provided.
   # TODO: As soon as supported, change collection: A to collection: A[B]
-  result = initTable[C, B]()
+  result = newTable[C, B]()
   for item in collection:
     result[index(item)] = item
 
-iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
-
-iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values
-  ## can be modified.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
-
-iterator keys*[A, B](t: TableRef[A, B]): A =
-  ## iterates over any key in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].key
+proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## One can check with `hasKey proc<#hasKey,TableRef[A,B],A>`_ whether
+  ## the key exists.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ for checking if a key is in
+  ##   the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
+  result = t[][key]
 
-iterator values*[A, B](t: TableRef[A, B]): B =
-  ## iterates over any value in the table ``t``.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].val
+proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `del proc<#del,TableRef[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = newTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.newTable
+  t[][key] = val
 
-iterator mvalues*[A, B](t: TableRef[A, B]): var B =
-  ## iterates over any value in the table ``t``. The values can be modified.
-  for h in 0..high(t.data):
-    if isFilled(t.data[h].hcode): yield t.data[h].val
+proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,TableRef[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  result = t[].hasKey(key)
 
-proc `[]`*[A, B](t: TableRef[A, B], key: A): var B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  result = t[][key]
+proc contains*[A, B](t: TableRef[A, B], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,TableRef[A,B],A>`_ for use with
+  ## the ``in`` operator.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  return hasKey[A, B](t, key)
 
-proc mget*[A, B](t: TableRef[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  t[][key]
+proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
+  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
+  t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
   ## default initialization value for type ``B`` is returned (e.g. 0 for any
   ## integer type).
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``.
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
   ## Otherwise, ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
   ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,TableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
   t[].mgetOrPut(key, val)
 
-proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  t[].hasKeyOrPut(key, val)
-
-proc contains*[A, B](t: TableRef[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
-
-proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  t[][key] = val
+proc len*[A, B](t: TableRef[A, B]): int =
+  ## Returns the number of keys in ``t``.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newTable
+    doAssert len(a) == 2
+  result = t.counter
 
 proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
-  ## puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
-  ## This can introduce duplicate keys into the table!
+  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
   t[].add(key, val)
 
 proc del*[A, B](t: TableRef[A, B], key: A) =
-  ## deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ##
+  ## See also:
+  ## * `take proc<#take,TableRef[A,B],A,B>`_
+  ## * `clear proc<#clear,TableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.newTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.newTable
   t[].del(key)
 
 proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
-  ## deletes the ``key`` from the table.
+  ## Deletes the ``key`` from the table.
   ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
   ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
   ## unchanged.
+  ##
+  ## See also:
+  ## * `del proc<#del,TableRef[A,B],A>`_
+  ## * `clear proc<#clear,TableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var
+      a = {'a': 5, 'b': 9, 'c': 13}.newTable
+      i: int
+    doAssert a.take('b', i) == true
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 9
+    i = 0
+    doAssert a.take('z', i) == false
+    doAssert a == {'a': 5, 'c': 13}.newTable
+    doAssert i == 0
   result = t[].take(key, val)
 
-proc newTable*[A, B](initialSize=64): TableRef[A, B] =
-  new(result)
-  result[] = initTable[A, B](initialSize)
-
-proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
-  ## creates a new hash table that contains the given ``pairs``.
-  new(result)
-  result[] = toTable[A, B](pairs)
+proc clear*[A, B](t: TableRef[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,Table[A,B],A>`_
+  ## * `take proc<#take,Table[A,B],A,B>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
 
 proc `$`*[A, B](t: TableRef[A, B]): string =
-  ## The ``$`` operator for hash tables.
+  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: TableRef[A, B]): bool =
-  ## The ``==`` operator for hash tables. Returns ``true`` iff either both tables
-  ## are ``nil`` or none is ``nil`` and the content of both tables contains the
+  ## The ``==`` operator for hash tables. Returns ``true`` if either both tables
+  ## are ``nil``, or neither is ``nil`` and the content of both tables contains the
   ## same key-value pairs. Insert order does not matter.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.newTable
+      b = {'b': 9, 'c': 13, 'a': 5}.newTable
+    doAssert a == b
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: equalsImpl(s[], t[])
 
-proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
-  ## Index the collection with the proc provided.
-  # TODO: As soon as supported, change collection: A to collection: A[B]
-  result = newTable[C, B]()
-  for item in collection:
-    result[index(item)] = item
 
-# ------------------------------ ordered table ------------------------------
 
-type
-  OrderedKeyValuePair[A, B] = tuple[
-    hcode: Hash, next: int, key: A, val: B]
-  OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
-  OrderedTable* [A, B] = object ## table that remembers insertion order
-    data: OrderedKeyValuePairSeq[A, B]
-    counter, first, last: int
-  OrderedTableRef*[A, B] = ref OrderedTable[A, B]
-
-proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.newTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
-proc clear*[A, B](t: var OrderedTable[A, B]) =
-  ## resets the table so that it is empty.
-  clearImpl()
-  t.first = -1
-  t.last = -1
+iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values
+  ## can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
-proc clear*[A, B](t: var OrderedTableRef[A, B]) =
-  ## resets the table so that is is empty.
-  clear(t[])
+iterator keys*[A, B](t: TableRef[A, B]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].key
 
-template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
-  var h = t.first
-  while h >= 0:
-    var nxt = t.data[h].next
-    if isFilled(t.data[h].hcode): yieldStmt
-    h = nxt
+iterator values*[A, B](t: TableRef[A, B]): B =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,TableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for v in a.values:
+      doAssert v.len == 4
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].val
 
-iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order.
-  forAllOrderedPairs:
-    yield (t.data[h].key, t.data[h].val)
+iterator mvalues*[A, B](t: TableRef[A, B]): var B =
+  ## Iterates over any value in the table ``t``. The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_
+  ## * `values iterator<#values.i,TableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+  for h in 0..high(t.data):
+    if isFilled(t.data[h].hcode): yield t.data[h].val
 
-iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order. The values can be modified.
-  forAllOrderedPairs:
-    yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A, B](t: OrderedTable[A, B]): A =
-  ## iterates over any key in the table ``t`` in insertion order.
-  forAllOrderedPairs:
-    yield t.data[h].key
 
-iterator values*[A, B](t: OrderedTable[A, B]): B =
-  ## iterates over any value in the table ``t`` in insertion order.
-  forAllOrderedPairs:
-    yield t.data[h].val
 
-iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
-  ## iterates over any value in the table ``t`` in insertion order. The values
-  ## can be modified.
-  forAllOrderedPairs:
-    yield t.data[h].val
 
-proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
-  rawGetKnownHCImpl()
 
-proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
-  rawGetDeepImpl()
 
-proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
-  rawGetImpl()
 
-proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
-  ## the key exists.
-  get(t, key)
+# ---------------------------------------------------------------------------
+# ------------------------------ OrderedTable -------------------------------
+# ---------------------------------------------------------------------------
 
-proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B{.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  get(t, key)
+type
+  OrderedKeyValuePair[A, B] = tuple[
+    hcode: Hash, next: int, key: A, val: B]
+  OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
+  OrderedTable* [A, B] = object
+    ## Hash table that remembers insertion order.
+    ##
+    ## For creating an empty OrderedTable, use `initOrderedTable proc
+    ## <#initOrderedTable,int>`_.
+    data: OrderedKeyValuePairSeq[A, B]
+    counter, first, last: int
+  OrderedTableRef*[A, B] = ref OrderedTable[A, B] ## Ref version of
+    ## `OrderedTable<#OrderedTable>`_.
+    ##
+    ## For creating a new empty OrderedTableRef, use `newOrderedTable proc
+    ## <#newOrderedTable,int>`_.
 
-proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  get(t, key)
 
-proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## default initialization value for type ``B`` is returned (e.g. 0 for any
-  ## integer type).
-  getOrDefaultImpl(t, key)
+# ------------------------------ helpers ---------------------------------
 
-proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise,
-  ## ``default`` is returned.
-  getOrDefaultImpl(t, key, default)
+proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
+  rawGetKnownHCImpl()
 
-proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  var hc: Hash
-  result = rawGet(t, key, hc) >= 0
+proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
+  rawGetDeepImpl()
 
-proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
+proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
+  rawGetImpl()
 
 proc rawInsert[A, B](t: var OrderedTable[A, B],
                      data: var OrderedKeyValuePairSeq[A, B],
@@ -639,30 +1205,32 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) =
       rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
     h = nxt
 
-proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  putImpl(enlarge)
-
-proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
-  ## puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
-  ## This can introduce duplicate keys into the table!
-  addImpl(enlarge)
+template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
+  var h = t.first
+  while h >= 0:
+    var nxt = t.data[h].next
+    if isFilled(t.data[h].hcode): yieldStmt
+    h = nxt
 
-proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``value`` if not present, either way
-  ## returning a value which can be modified.
-  mgetOrPutImpl(enlarge)
-
-proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``value``.
-  hasKeyOrPutImpl(enlarge)
+# ----------------------------------------------------------------------
 
 proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
-  ## creates a new ordered hash table that is empty.
+  ## Creates a new ordered hash table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
   ##
-  ## ``initialSize`` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
+  ## See also:
+  ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
+  ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
+  ##   `OrderedTableRef`
+  runnableExamples:
+    let
+      a = initOrderedTable[int, string]()
+      b = initOrderedTable[char, seq[int]]()
   assert isPowerOfTwo(initialSize)
   result.counter = 0
   result.first = -1
@@ -670,36 +1238,250 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
   newSeq(result.data, initialSize)
 
 proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
-  ## creates a new ordered hash table that contains the given ``pairs``.
+  ## Creates a new ordered hash table that contains the given ``pairs``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `initOrderedTable proc<#initOrderedTable,int>`_
+  ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for an
+  ##   `OrderedTableRef` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = toOrderedTable(a)
+    assert b == {'a': 5, 'b': 9}.toOrderedTable
   result = initOrderedTable[A, B](rightSize(pairs.len))
   for key, val in items(pairs): result[key] = val
 
-proc `$`*[A, B](t: OrderedTable[A, B]): string =
-  ## The ``$`` operator for ordered hash tables.
-  dollarImpl()
+proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## One can check with `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ whether
+  ## the key exists.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a
+  ##   key is in the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
+  get(t, key)
 
-proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
-  ## The ``==`` operator for ordered hash tables. Returns true iff both the
-  ## content and the order are equal.
-  if s.counter != t.counter:
-    return false
-  var ht = t.first
-  var hs = s.first
-  while ht >= 0 and hs >= 0:
-    var nxtt = t.data[ht].next
-    var nxts = s.data[hs].next
-    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
-      if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val):
-        return false
-    ht = nxtt
-    hs = nxts
-  return true
+proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B=
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
+  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a
+  ##   key is in the table
+  get(t, key)
+
+proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `del proc<#del,OrderedTable[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = initOrderedTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.toOrderedTable
+  putImpl(enlarge)
+
+proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,OrderedTable[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  var hc: Hash
+  result = rawGet(t, key, hc) >= 0
+
+proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for use with
+  ## the ``in`` operator.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  return hasKey[A, B](t, key)
+
+proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
+  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toOrderedTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
+  hasKeyOrPutImpl(enlarge)
+
+proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
+  ## default initialization value for type ``B`` is returned (e.g. 0 for any
+  ## integer type).
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
+  getOrDefaultImpl(t, key)
+
+proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
+  ## Otherwise, ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTable[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
+  getOrDefaultImpl(t, key, default)
+
+proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
+  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTable[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
+  mgetOrPutImpl(enlarge)
+
+proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
+  ## Returns the number of keys in ``t``.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.toOrderedTable
+    doAssert len(a) == 2
+  result = t.counter
+
+proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
+  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
+  addImpl(enlarge)
+
+proc del*[A, B](t: var OrderedTable[A, B], key: A) =
+  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
+  ##
+  ## O(n) complexity.
+  ##
+  ## See also:
+  ## * `clear proc<#clear,OrderedTable[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.toOrderedTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.toOrderedTable
+  var n: OrderedKeyValuePairSeq[A, B]
+  newSeq(n, len(t.data))
+  var h = t.first
+  t.first = -1
+  t.last = -1
+  swap(t.data, n)
+  let hc = genHash(key)
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      if n[h].hcode == hc and n[h].key == key:
+        dec t.counter
+      else:
+        var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
+        rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
+    h = nxt
+
+proc clear*[A, B](t: var OrderedTable[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTable[A,B],A>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clearImpl()
+  t.first = -1
+  t.last = -1
 
 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
-  ## sorts ``t`` according to ``cmp``. This modifies the internal list
+  ## Sorts ``t`` according to the function ``cmp``.
+  ##
+  ## This modifies the internal list
   ## that kept the insertion order, so insertion order is lost after this
   ## call but key lookup and insertions remain possible after ``sort`` (in
-  ## contrast to the ``sort`` for count tables).
+  ## contrast to the `sort proc<#sort,CountTable[A]>`_ for count tables).
+  runnableExamples:
+    var a = initOrderedTable[char, int]()
+    for i, c in "cab":
+      a[c] = 10*i
+    doAssert a == {'c': 0, 'a': 10, 'b': 20}.toOrderedTable
+    a.sort(system.cmp)
+    doAssert a == {'a': 10, 'b': 20, 'c': 0}.toOrderedTable
+
   var list = t.first
   var
     p, q, e, tail, oldhead: int
@@ -740,244 +1522,519 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) =
   t.first = list
   t.last = tail
 
-proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc `$`*[A, B](t: OrderedTable[A, B]): string =
+  ## The ``$`` operator for ordered hash tables. Used internally when calling
+  ## `echo` on a table.
+  dollarImpl()
 
-iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
+  ## The ``==`` operator for ordered hash tables. Returns ``true`` if both the
+  ## content and the order are equal.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.toOrderedTable
+      b = {'b': 9, 'c': 13, 'a': 5}.toOrderedTable
+    doAssert a != b
+
+  if s.counter != t.counter:
+    return false
+  var ht = t.first
+  var hs = s.first
+  while ht >= 0 and hs >= 0:
+    var nxtt = t.data[ht].next
+    var nxts = s.data[hs].next
+    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
+      if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val):
+        return false
+    ht = nxtt
+    hs = nxts
+  return true
+
+
+
+iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
   ## order.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTable[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.toOrderedTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
-  ## iterates over any ``(key, value)`` pair in the table ``t`` in insertion
-  ## order. The values can be modified.
+iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## declared as `var`) in insertion order. The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
-  ## iterates over any key in the table ``t`` in insertion order.
+iterator keys*[A, B](t: OrderedTable[A, B]): A =
+  ## Iterates over any key in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
   forAllOrderedPairs:
     yield t.data[h].key
 
-iterator values*[A, B](t: OrderedTableRef[A, B]): B =
-  ## iterates over any value in the table ``t`` in insertion order.
+iterator values*[A, B](t: OrderedTable[A, B]): B =
+  ## Iterates over any value in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTable[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for v in a.values:
+      doAssert v.len == 4
   forAllOrderedPairs:
     yield t.data[h].val
 
-iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
-  ## iterates over any value in the table ``t`` in insertion order. The values
+iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
+  ## Iterates over any value in the table ``t`` (must be
+  ## declared as `var`) in insertion order. The values
   ## can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTable[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTable[A,B]>`_
+  runnableExamples:
+    var a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.toOrderedTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
   forAllOrderedPairs:
     yield t.data[h].val
 
+
+
+
+
+# ---------------------------------------------------------------------------
+# --------------------------- OrderedTableRef -------------------------------
+# ---------------------------------------------------------------------------
+
+proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
+  ## Creates a new ordered ref hash table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
+  ##
+  ## See also:
+  ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating
+  ##   an `OrderedTableRef` from a collection of `(key, value)` pairs
+  ## * `initOrderedTable proc<#initOrderedTable,int>`_ for creating an
+  ##   `OrderedTable`
+  runnableExamples:
+    let
+      a = newOrderedTable[int, string]()
+      b = newOrderedTable[char, seq[int]]()
+  new(result)
+  result[] = initOrderedTable[A, B](initialSize)
+
+proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
+  ## Creates a new ordered ref hash table that contains the given ``pairs``.
+  ##
+  ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
+  ##
+  ## See also:
+  ## * `newOrderedTable proc<#newOrderedTable,int>`_
+  ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ for an
+  ##   `OrderedTable` version
+  runnableExamples:
+    let a = [('a', 5), ('b', 9)]
+    let b = newOrderedTable(a)
+    assert b == {'a': 5, 'b': 9}.newOrderedTable
+  result = newOrderedTable[A, B](rightSize(pairs.len))
+  for key, val in items(pairs): result.add(key, val)
+
+
 proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``, the
-  ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
+  ## Retrieves the value at ``t[key]``.
+  ##
+  ## If ``key`` is not in ``t``, the  ``KeyError`` exception is raised.
+  ## One can check with `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ whether
   ## the key exists.
+  ##
+  ## See also:
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for checking if
+  ##   a key is in the table
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a['a'] == 5
+    doAssertRaises(KeyError):
+      echo a['z']
   result = t[][key]
 
-proc mget*[A, B](t: OrderedTableRef[A, B], key: A): var B {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  result = t[][key]
+proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `del proc<#del,OrderedTableRef[A,B],A>`_ for removing a key from the table
+  runnableExamples:
+    var a = newOrderedTable[char, int]()
+    a['x'] = 7
+    a['y'] = 33
+    doAssert a == {'x': 7, 'y': 33}.newOrderedTable
+  t[][key] = val
+
+proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,OrderedTableRef[A,B],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.hasKey('a') == true
+    doAssert a.hasKey('z') == false
+  result = t[].hasKey(key)
+
+proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for use with
+  ## the ``in`` operator.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert 'b' in a == true
+    doAssert a.contains('z') == false
+  return hasKey[A, B](t, key)
+
+proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
+  ## Returns true if ``key`` is in the table, otherwise inserts ``value``.
+  ##
+  ## See also:
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newOrderedTable
+    if a.hasKeyOrPut('a', 50):
+      a['a'] = 99
+    if a.hasKeyOrPut('z', 50):
+      a['z'] = 99
+    doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
+  result = t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the
   ## default initialization value for type ``B`` is returned (e.g. 0 for any
   ## integer type).
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.getOrDefault('a') == 5
+    doAssert a.getOrDefault('z') == 0
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise,
-  ## ``default`` is returned.
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
+  ## Otherwise, ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `mgetOrPut proc<#mgetOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.getOrDefault('a', 99) == 5
+    doAssert a.getOrDefault('z', 99) == 99
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
-  ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way
+  ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way
   ## returning a value which can be modified.
+  ##
+  ## See also:
+  ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_
+  ## * `hasKeyOrPut proc<#hasKeyOrPut,OrderedTableRef[A,B],A,B>`_
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A>`_ to return
+  ##   a default value (e.g. zero for int) if the key doesn't exist
+  ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return
+  ##   a custom value if the key doesn't exist
+  runnableExamples:
+    var a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert a.mgetOrPut('a', 99) == 5
+    doAssert a.mgetOrPut('z', 99) == 99
+    doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
   result = t[].mgetOrPut(key, val)
 
-proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
-  ## returns true iff ``key`` is in the table, otherwise inserts ``val``.
-  result = t[].hasKeyOrPut(key, val)
-
-proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
-
-proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A, B](t, key)
-
-proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## puts a ``(key, value)`` pair into ``t``.
-  t[][key] = val
+proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
+  ## Returns the number of keys in ``t``.
+  runnableExamples:
+    let a = {'a': 5, 'b': 9}.newOrderedTable
+    doAssert len(a) == 2
+  result = t.counter
 
 proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
-  ## puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
-  ## This can introduce duplicate keys into the table!
+  ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists.
+  ##
+  ## **This can introduce duplicate keys into the table!**
+  ##
+  ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new
+  ## (key, value) pair in the table without introducing duplicates.
   t[].add(key, val)
 
-proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
-  ## creates a new ordered hash table that is empty.
+proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
+  ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
   ##
-  ## ``initialSize`` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc from this module.
-  new(result)
-  result[] = initOrderedTable[A, B](initialSize)
+  ## See also:
+  ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+    a.del('a')
+    doAssert a == {'b': 9, 'c': 13}.newOrderedTable
+    a.del('z')
+    doAssert a == {'b': 9, 'c': 13}.newOrderedTable
+  t[].del(key)
 
-proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
-  ## creates a new ordered hash table that contains the given ``pairs``.
-  result = newOrderedTable[A, B](rightSize(pairs.len))
-  for key, val in items(pairs): result.add(key, val)
+proc clear*[A, B](t: var OrderedTableRef[A, B]) =
+  ## Resets the table so that it is empty.
+  ##
+  ## See also:
+  ## * `del proc<#del,OrderedTable[A,B],A>`_
+  runnableExamples:
+    var a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+    doAssert len(a) == 3
+    clear(a)
+    doAssert len(a) == 0
+  clear(t[])
+
+proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) =
+  ## Sorts ``t`` according to the function ``cmp``.
+  ##
+  ## This modifies the internal list
+  ## that kept the insertion order, so insertion order is lost after this
+  ## call but key lookup and insertions remain possible after ``sort`` (in
+  ## contrast to the `sort proc<#sort,CountTableRef[A]>`_ for count tables).
+  runnableExamples:
+    var a = newOrderedTable[char, int]()
+    for i, c in "cab":
+      a[c] = 10*i
+    doAssert a == {'c': 0, 'a': 10, 'b': 20}.newOrderedTable
+    a.sort(system.cmp)
+    doAssert a == {'a': 10, 'b': 20, 'c': 0}.newOrderedTable
+  t[].sort(cmp)
 
 proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
-  ## The ``$`` operator for ordered hash tables.
+  ## The ``$`` operator for hash tables. Used internally when calling `echo`
+  ## on a table.
   dollarImpl()
 
 proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
-  ## The ``==`` operator for ordered hash tables. Returns true iff either both
-  ## tables are ``nil`` or none is ``nil`` and the content and the order of
+  ## The ``==`` operator for ordered hash tables. Returns true if either both
+  ## tables are ``nil``, or neither is ``nil`` and the content and the order of
   ## both are equal.
+  runnableExamples:
+    let
+      a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
+      b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
+    doAssert a != b
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: result = s[] == t[]
 
-proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) =
-  ## sorts ``t`` according to ``cmp``. This modifies the internal list
-  ## that kept the insertion order, so insertion order is lost after this
-  ## call but key lookup and insertions remain possible after ``sort`` (in
-  ## contrast to the ``sort`` for count tables).
-  t[].sort(cmp)
-
-proc del*[A, B](t: var OrderedTable[A, B], key: A) =
-  ## deletes ``key`` from ordered hash table ``t``. O(n) complexity. Does nothing
-  ## if the key does not exist.
-  var n: OrderedKeyValuePairSeq[A, B]
-  newSeq(n, len(t.data))
-  var h = t.first
-  t.first = -1
-  t.last = -1
-  swap(t.data, n)
-  let hc = genHash(key)
-  while h >= 0:
-    var nxt = n[h].next
-    if isFilled(n[h].hcode):
-      if n[h].hcode == hc and n[h].key == key:
-        dec t.counter
-      else:
-        var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode)
-        rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
-    h = nxt
-
-proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
-  ## deletes ``key`` from ordered hash table ``t``. O(n) complexity.  Does nothing
-  ## if the key does not exist.
-  t[].del(key)
-
-# ------------------------------ count tables -------------------------------
-
-type
-  CountTable* [
-      A] = object ## table that counts the number of each key
-    data: seq[tuple[key: A, val: int]]
-    counter: int
-  CountTableRef*[A] = ref CountTable[A]
-
-proc len*[A](t: CountTable[A]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
 
-proc clear*[A](t: CountTableRef[A]) =
-  ## resets the table so that it is empty.
-  clearImpl()
 
-proc clear*[A](t: var CountTable[A]) =
-  ## resets the table so that it is empty.
-  clearImpl()
+iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## order.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = {
+  ##     'o': [1, 5, 7, 9],
+  ##     'e': [2, 4, 6, 8]
+  ##     }.newOrderedTable
+  ##
+  ##   for k, v in a.pairs:
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: o
+  ##   # value: [1, 5, 7, 9]
+  ##   # key: e
+  ##   # value: [2, 4, 6, 8]
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
 
-iterator pairs*[A](t: CountTable[A]): (A, int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion
+  ## order. The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for k, v in a.mpairs:
+      v.add(v[0] + 10)
+    doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
+  forAllOrderedPairs:
+    yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values can
-  ## be modified.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
+  ## Iterates over any key in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for k in a.keys:
+      a[k].add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+  forAllOrderedPairs:
+    yield t.data[h].key
 
-iterator keys*[A](t: CountTable[A]): A =
-  ## iterates over any key in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].key
+iterator values*[A, B](t: OrderedTableRef[A, B]): B =
+  ## Iterates over any value in the table ``t`` in insertion order.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_
+  ## * `keys iterator<#keys.i,OrderedTableRef[A,B]>`_
+  ## * `mvalues iterator<#mvalues.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for v in a.values:
+      doAssert v.len == 4
+  forAllOrderedPairs:
+    yield t.data[h].val
 
-iterator values*[A](t: CountTable[A]): int =
-  ## iterates over any value in the table ``t``.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].val
+iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
+  ## Iterates over any value in the table ``t`` in insertion order. The values
+  ## can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,OrderedTableRef[A,B]>`_
+  ## * `values iterator<#values.i,OrderedTableRef[A,B]>`_
+  runnableExamples:
+    let a = {
+      'o': @[1, 5, 7, 9],
+      'e': @[2, 4, 6, 8]
+      }.newOrderedTable
+    for v in a.mvalues:
+      v.add(99)
+    doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+  forAllOrderedPairs:
+    yield t.data[h].val
 
-iterator mvalues*[A](t: CountTable[A]): var int =
-  ## iterates over any value in the table ``t``. The values can be modified.
-  for h in 0..high(t.data):
-    if t.data[h].val != 0: yield t.data[h].val
 
-proc rawGet[A](t: CountTable[A], key: A): int =
-  var h: Hash = hash(key) and high(t.data) # start with real hash value
-  while t.data[h].val != 0:
-    if t.data[h].key == key: return h
-    h = nextTry(h, high(t.data))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
 
-template ctget(t, key: untyped): untyped =
-  var index = rawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else:
-    when compiles($key):
-      raise newException(KeyError, "key not found: " & $key)
-    else:
-      raise newException(KeyError, "key not found")
 
-proc `[]`*[A](t: CountTable[A], key: A): int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. If ``key`` is not in ``t``,
-  ## the ``KeyError`` exception is raised. One can check with ``hasKey``
-  ## whether the key exists.
-  ctget(t, key)
 
-proc `[]`*[A](t: var CountTable[A], key: A): var int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ctget(t, key)
 
-proc mget*[A](t: var CountTable[A], key: A): var int {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  ctget(t, key)
 
-proc getOrDefault*[A](t: CountTable[A], key: A): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, 0 (the
-  ## default initialization value of ``int``), is returned.
-  var index = rawGet(t, key)
-  if index >= 0: result = t.data[index].val
+# -------------------------------------------------------------------------
+# ------------------------------ CountTable -------------------------------
+# -------------------------------------------------------------------------
 
-proc getOrDefault*[A](t: CountTable[A], key: A, default: int): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
-  var index = rawGet(t, key)
-  result = if index >= 0: t.data[index].val else: default
+type
+  CountTable* [A] = object
+    ## Hash table that counts the number of each key.
+    ##
+    ## For creating an empty CountTable, use `initCountTable proc
+    ## <#initCountTable,int>`_.
+    data: seq[tuple[key: A, val: int]]
+    counter: int
+  CountTableRef*[A] = ref CountTable[A] ## Ref version of
+    ## `CountTable<#CountTable>`_.
+    ##
+    ## For creating a new empty CountTableRef, use `newCountTable proc
+    ## <#newCountTable,int>`_.
 
-proc hasKey*[A](t: CountTable[A], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = rawGet(t, key) >= 0
 
-proc contains*[A](t: CountTable[A], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A](t, key)
+# ------------------------------ helpers ---------------------------------
 
 proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
                   key: A, val: int) =
@@ -993,22 +2050,87 @@ proc enlarge[A](t: var CountTable[A]) =
     if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
+proc rawGet[A](t: CountTable[A], key: A): int =
+  var h: Hash = hash(key) and high(t.data) # start with real hash value
+  while t.data[h].val != 0:
+    if t.data[h].key == key: return h
+    h = nextTry(h, high(t.data))
+  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
+
+template ctget(t, key, default: untyped): untyped =
+  var index = rawGet(t, key)
+  result = if index >= 0: t.data[index].val else: default
+
+proc inc*[A](t: var CountTable[A], key: A, val = 1)
+
+# ----------------------------------------------------------------------
+
+proc initCountTable*[A](initialSize=64): CountTable[A] =
+  ## Creates a new count table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
+  ##
+  ## See also:
+  ## * `toCountTable proc<#toCountTable,openArray[A]>`_
+  ## * `newCountTable proc<#newCountTable,int>`_ for creating a
+  ##   `CountTableRef`
+  assert isPowerOfTwo(initialSize)
+  result.counter = 0
+  newSeq(result.data, initialSize)
+
+proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
+  ## Creates a new count table with every member of a container ``keys``
+  ## having a count of how many times it occurs in that container.
+  result = initCountTable[A](rightSize(keys.len))
+  for key in items(keys): result.inc(key)
+
+proc `[]`*[A](t: CountTable[A], key: A): int =
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
+  ## Otherwise ``0`` is returned.
+  ##
+  ## See also:
+  ## * `getOrDefault<#getOrDefault,CountTable[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `mget proc<#mget,CountTable[A],A>`_
+  ## * `[]= proc<#[]%3D,CountTable[A],A,int>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key
+  ##   is in the table
+  ctget(t, key, 0)
+
+proc mget*[A](t: var CountTable[A], key: A): var int =
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
+  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  get(t, key)
+
 proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
-  ## puts a ``(key, value)`` pair into ``t``.
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
+  ## * `inc proc<#inc,CountTable[A],A,int>`_ for incrementing a
+  ##   value of a key
   assert val >= 0
-  var h = rawGet(t, key)
+  let h = rawGet(t, key)
   if h >= 0:
     t.data[h].val = val
   else:
     if mustRehash(len(t.data), t.counter): enlarge(t)
     rawInsert(t, t.data, key, val)
     inc(t.counter)
-    #h = -1 - h
-    #t.data[h].key = key
-    #t.data[h].val = val
 
 proc inc*[A](t: var CountTable[A], key: A, val = 1) =
-  ## increments ``t[key]`` by ``val``.
+  ## Increments ``t[key]`` by ``val`` (default: 1).
+  runnableExamples:
+    var a = toCountTable("aab")
+    a.inc('a')
+    a.inc('b', 10)
+    doAssert a == toCountTable("aaabbbbbbbbbbb")
   var index = rawGet(t, key)
   if index >= 0:
     inc(t.data[index].val, val)
@@ -1018,33 +2140,11 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
     rawInsert(t, t.data, key, val)
     inc(t.counter)
 
-proc initCountTable*[A](initialSize=64): CountTable[A] =
-  ## creates a new count table that is empty.
-  ##
-  ## ``initialSize`` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` proc in this module.
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
-
-proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
-  ## creates a new count table with every key in ``keys`` having a count
-  ## of how many times it occurs in ``keys``.
-  result = initCountTable[A](rightSize(keys.len))
-  for key in items(keys): result.inc(key)
-
-proc `$`*[A](t: CountTable[A]): string =
-  ## The ``$`` operator for count tables.
-  dollarImpl()
-
-proc `==`*[A](s, t: CountTable[A]): bool =
-  ## The ``==`` operator for count tables. Returns ``true`` iff both tables
-  ## contain the same keys with the same count. Insert order does not matter.
-  equalsImpl(s, t)
-
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
+  ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `largest proc<#largest,CountTable[A]>`_
   assert t.len > 0
   var minIdx = -1
   for h in 0..high(t.data):
@@ -1054,7 +2154,10 @@ proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   result.val = t.data[minIdx].val
 
 proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
+  ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `smallest proc<#smallest,CountTable[A]>`_
   assert t.len > 0
   var maxIdx = 0
   for h in 1..high(t.data):
@@ -1062,11 +2165,49 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   result.key = t.data[maxIdx].key
   result.val = t.data[maxIdx].val
 
+proc hasKey*[A](t: CountTable[A], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,CountTable[A],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,CountTable[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  result = rawGet(t, key) >= 0
+
+proc contains*[A](t: CountTable[A], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,CountTable[A],A>`_ for use with
+  ## the ``in`` operator.
+  return hasKey[A](t, key)
+
+proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int =
+  ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the
+  ## integer value of ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key
+  ##   is in the table
+  ctget(t, key, default)
+
+proc len*[A](t: CountTable[A]): int =
+  ## Returns the number of keys in ``t``.
+  result = t.counter
+
+proc clear*[A](t: var CountTable[A]) =
+  ## Resets the table so that it is empty.
+  clearImpl()
+
 proc sort*[A](t: var CountTable[A]) =
-  ## sorts the count table so that the entry with the highest counter comes
-  ## first. This is destructive! You must not modify ``t`` afterwards!
-  ## You can use the iterators ``pairs``, ``keys``, and ``values`` to iterate over
-  ## ``t`` in the sorted order.
+  ## Sorts the count table so that the entry with the highest counter comes
+  ## first.
+  ##
+  ## **This is destructive! You must not modify ``t`` afterwards!**
+  ##
+  ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_,
+  ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_
+  ## to iterate over ``t`` in the sorted order.
 
   # we use shellsort here; fast enough and simple
   var h = 1
@@ -1083,131 +2224,373 @@ proc sort*[A](t: var CountTable[A]) =
         if j < h: break
     if h == 1: break
 
-proc len*[A](t: CountTableRef[A]): int =
-  ## returns the number of keys in ``t``.
-  result = t.counter
+proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
+  ## Merges the second table into the first one (must be declared as `var`).
+  runnableExamples:
+    var a = toCountTable("aaabbc")
+    let b = toCountTable("bcc")
+    a.merge(b)
+    doAssert a == toCountTable("aaabbbccc")
+  for key, value in t:
+    s.inc(key, value)
 
-iterator pairs*[A](t: CountTableRef[A]): (A, int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``.
+proc merge*[A](s, t: CountTable[A]): CountTable[A] =
+  ## Merges the two tables into a new one.
+  runnableExamples:
+    let
+      a = toCountTable("aaabbc")
+      b = toCountTable("bcc")
+    doAssert merge(a, b) == toCountTable("aaabbbccc")
+  result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
+  for table in @[s, t]:
+    for key, value in table:
+      result.inc(key, value)
+
+proc `$`*[A](t: CountTable[A]): string =
+  ## The ``$`` operator for count tables. Used internally when calling `echo`
+  ## on a table.
+  dollarImpl()
+
+proc `==`*[A](s, t: CountTable[A]): bool =
+  ## The ``==`` operator for count tables. Returns ``true`` if both tables
+  ## contain the same keys with the same count. Insert order does not matter.
+  equalsImpl(s, t)
+
+
+iterator pairs*[A](t: CountTable[A]): (A, int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_
+  ## * `keys iterator<#keys.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = toCountTable("abracadabra")
+  ##
+  ##   for k, v in pairs(a):
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: a
+  ##   # value: 5
+  ##   # key: b
+  ##   # value: 2
+  ##   # key: c
+  ##   # value: 1
+  ##   # key: d
+  ##   # value: 1
+  ##   # key: r
+  ##   # value: 2
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
-  ## iterates over any ``(key, value)`` pair in the table ``t``. The values can
-  ## be modified.
+iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for k, v in mpairs(a):
+      v = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-iterator keys*[A](t: CountTableRef[A]): A =
-  ## iterates over any key in the table ``t``.
+iterator keys*[A](t: CountTable[A]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for k in keys(a):
+      a[k] = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].key
 
-iterator values*[A](t: CountTableRef[A]): int =
-  ## iterates over any value in the table ``t``.
+iterator values*[A](t: CountTable[A]): int =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `keys iterator<#keys.i,CountTable[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTable[A]>`_
+  runnableExamples:
+    let a = toCountTable("abracadabra")
+    for v in values(a):
+      assert v < 10
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
-iterator mvalues*[A](t: CountTableRef[A]): var int =
-  ## iterates over any value in the table ``t``. The values can be modified.
+iterator mvalues*[A](t: var CountTable[A]): var int =
+  ## Iterates over any value in the table ``t`` (must be
+  ## declared as `var`). The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    var a = toCountTable("abracadabra")
+    for v in mvalues(a):
+      v = 2
+    doAssert a == toCountTable("aabbccddrr")
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
-proc `[]`*[A](t: CountTableRef[A], key: A): var int {.deprecatedGet.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  result = t[][key]
 
-proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
-  ## retrieves the value at ``t[key]``. The value can be modified.
-  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
-  ## Use ``[]`` instead.
-  result = t[][key]
 
-proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, 0 (the
-  ## default initialization value of ``int``), is returned.
-  result = t[].getOrDefault(key)
 
-proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
-  ## retrieves the value at ``t[key]`` iff ``key`` is in ``t``. Otherwise, the
-  ## integer value of ``default`` is returned.
-  result = t[].getOrDefault(key, default)
 
-proc hasKey*[A](t: CountTableRef[A], key: A): bool =
-  ## returns true iff ``key`` is in the table ``t``.
-  result = t[].hasKey(key)
 
-proc contains*[A](t: CountTableRef[A], key: A): bool =
-  ## Alias of ``hasKey`` for use with the ``in`` operator.
-  return hasKey[A](t, key)
 
-proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
-  ## puts a ``(key, value)`` pair into ``t``. ``val`` has to be positive.
-  assert val > 0
-  t[][key] = val
+# ---------------------------------------------------------------------------
+# ---------------------------- CountTableRef --------------------------------
+# ---------------------------------------------------------------------------
 
-proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
-  ## increments ``t[key]`` by ``val``.
-  t[].inc(key, val)
+proc inc*[A](t: CountTableRef[A], key: A, val = 1)
 
 proc newCountTable*[A](initialSize=64): CountTableRef[A] =
-  ## creates a new count table that is empty.
+  ## Creates a new ref count table that is empty.
+  ##
+  ## ``initialSize`` must be a power of two (default: 64).
+  ## If you need to accept runtime values for this you could use the
+  ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
+  ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
+  ## from this module.
   ##
-  ## ``initialSize`` needs to be a power of two. If you need to accept runtime
-  ## values for this you could use the ``nextPowerOfTwo`` proc from the
-  ## `math <math.html>`_ module or the ``rightSize`` method in this module.
+  ## See also:
+  ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating
+  ##   a `CountTableRef` from a collection
+  ## * `initCountTable proc<#initCountTable,int>`_ for creating a
+  ##   `CountTable`
   new(result)
   result[] = initCountTable[A](initialSize)
 
 proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
-  ## creates a new count table with every key in ``keys`` having a count
-  ## of how many times it occurs in ``keys``.
+  ## Creates a new ref count table with every member of a container ``keys``
+  ## having a count of how many times it occurs in that container.
   result = newCountTable[A](rightSize(keys.len))
   for key in items(keys): result.inc(key)
 
+proc `[]`*[A](t: CountTableRef[A], key: A): int =
+  ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``.
+  ## Otherwise ``0`` is returned.
+  ##
+  ## See also:
+  ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  ## * `mget proc<#mget,CountTableRef[A],A>`_
+  ## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new
+  ##   (key, value) pair in the table
+  ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key
+  ##   is in the table
+  result = t[][key]
+
+proc mget*[A](t: CountTableRef[A], key: A): var int =
+  ## Retrieves the value at ``t[key]``. The value can be modified.
+  ##
+  ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised.
+  mget(t[], key)
+
+proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
+  ## Inserts a ``(key, value)`` pair into ``t``.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
+  ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a
+  ##   value of a key
+  assert val > 0
+  t[][key] = val
+
+proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
+  ## Increments ``t[key]`` by ``val`` (default: 1).
+  runnableExamples:
+    var a = newCountTable("aab")
+    a.inc('a')
+    a.inc('b', 10)
+    doAssert a == newCountTable("aaabbbbbbbbbbb")
+  t[].inc(key, val)
+
+proc smallest*[A](t: CountTableRef[A]): (A, int) =
+  ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `largest proc<#largest,CountTableRef[A]>`_
+  t[].smallest
+
+proc largest*[A](t: CountTableRef[A]): (A, int) =
+  ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
+  ##
+  ## See also:
+  ## * `smallest proc<#smallest,CountTable[A]>`_
+  t[].largest
+
+proc hasKey*[A](t: CountTableRef[A], key: A): bool =
+  ## Returns true if ``key`` is in the table ``t``.
+  ##
+  ## See also:
+  ## * `contains proc<#contains,CountTableRef[A],A>`_ for use with the `in`
+  ##   operator
+  ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
+  ## * `getOrDefault proc<#getOrDefault,CountTableRef[A],A,int>`_ to return
+  ##   a custom value if the key doesn't exist
+  result = t[].hasKey(key)
+
+proc contains*[A](t: CountTableRef[A], key: A): bool =
+  ## Alias of `hasKey proc<#hasKey,CountTableRef[A],A>`_ for use with
+  ## the ``in`` operator.
+  return hasKey[A](t, key)
+
+proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
+  ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the
+  ## integer value of ``default`` is returned.
+  ##
+  ## See also:
+  ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key
+  ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key
+  ##   is in the table
+  result = t[].getOrDefault(key, default)
+
+proc len*[A](t: CountTableRef[A]): int =
+  ## Returns the number of keys in ``t``.
+  result = t.counter
+
+proc clear*[A](t: CountTableRef[A]) =
+  ## Resets the table so that it is empty.
+  clearImpl()
+
+proc sort*[A](t: CountTableRef[A]) =
+  ## Sorts the count table so that the entry with the highest counter comes
+  ## first.
+  ##
+  ## **This is destructive! You must not modify `t` afterwards!**
+  ##
+  ## You can use the iterators `pairs<#pairs.i,CountTableRef[A]>`_,
+  ## `keys<#keys.i,CountTableRef[A]>`_, and `values<#values.i,CountTableRef[A]>`_
+  ## to iterate over ``t`` in the sorted order.
+  t[].sort
+
+proc merge*[A](s, t: CountTableRef[A]) =
+  ## Merges the second table into the first one.
+  runnableExamples:
+    let
+      a = newCountTable("aaabbc")
+      b = newCountTable("bcc")
+    a.merge(b)
+    doAssert a == newCountTable("aaabbbccc")
+  s[].merge(t[])
+
 proc `$`*[A](t: CountTableRef[A]): string =
-  ## The ``$`` operator for count tables.
+  ## The ``$`` operator for count tables. Used internally when calling `echo`
+  ## on a table.
   dollarImpl()
 
 proc `==`*[A](s, t: CountTableRef[A]): bool =
-  ## The ``==`` operator for count tables. Returns ``true`` iff either both tables
-  ## are ``nil`` or none is ``nil`` and both contain the same keys with the same
+  ## The ``==`` operator for count tables. Returns ``true`` if either both tables
+  ## are ``nil``, or neither is ``nil`` and both contain the same keys with the same
   ## count. Insert order does not matter.
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: result = s[] == t[]
 
-proc smallest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
-  t[].smallest
 
-proc largest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n)
-  t[].largest
+iterator pairs*[A](t: CountTableRef[A]): (A, int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
+  ## * `keys iterator<#keys.i,CountTableRef[A]>`_
+  ## * `values iterator<#values.i,CountTableRef[A]>`_
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block::
+  ##   let a = newCountTable("abracadabra")
+  ##
+  ##   for k, v in pairs(a):
+  ##     echo "key: ", k
+  ##     echo "value: ", v
+  ##
+  ##   # key: a
+  ##   # value: 5
+  ##   # key: b
+  ##   # value: 2
+  ##   # key: c
+  ##   # value: 1
+  ##   # key: d
+  ##   # value: 1
+  ##   # key: r
+  ##   # value: 2
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
-proc sort*[A](t: CountTableRef[A]) =
-  ## sorts the count table so that the entry with the highest counter comes
-  ## first. This is destructive! You must not modify ``t`` afterwards!
-  ## You can use the iterators ``pairs``, ``keys``, and ``values`` to iterate over
-  ## ``t`` in the sorted order.
-  t[].sort
+iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
+  ## Iterates over any ``(key, value)`` pair in the table ``t``. The values can
+  ## be modified.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTableRef[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for k, v in mpairs(a):
+      v = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
+
+iterator keys*[A](t: CountTableRef[A]): A =
+  ## Iterates over any key in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTable[A]>`_
+  ## * `values iterator<#values.i,CountTable[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for k in keys(a):
+      a[k] = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].key
+
+iterator values*[A](t: CountTableRef[A]): int =
+  ## Iterates over any value in the table ``t``.
+  ##
+  ## See also:
+  ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_
+  ## * `keys iterator<#keys.i,CountTableRef[A]>`_
+  ## * `mvalues iterator<#mvalues.i,CountTableRef[A]>`_
+  runnableExamples:
+    let a = newCountTable("abracadabra")
+    for v in values(a):
+      assert v < 10
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].val
+
+iterator mvalues*[A](t: CountTableRef[A]): var int =
+  ## Iterates over any value in the table ``t``. The values can be modified.
+  ##
+  ## See also:
+  ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_
+  ## * `values iterator<#values.i,CountTableRef[A]>`_
+  runnableExamples:
+    var a = newCountTable("abracadabra")
+    for v in mvalues(a):
+      v = 2
+    doAssert a == newCountTable("aabbccddrr")
+  for h in 0..high(t.data):
+    if t.data[h].val != 0: yield t.data[h].val
 
-proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
-  ## merges the second table into the first one.
-  for key, value in t:
-    s.inc(key, value)
 
-proc merge*[A](s, t: CountTable[A]): CountTable[A] =
-  ## merges the two tables into a new one.
-  result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
-  for table in @[s, t]:
-    for key, value in table:
-      result.inc(key, value)
 
-proc merge*[A](s, t: CountTableRef[A]) =
-  ## merges the second table into the first one.
-  s[].merge(t[])
 
 when isMainModule:
   type
@@ -1325,9 +2708,9 @@ when isMainModule:
     #test_counttable.nim(7, 43) template/generic instantiation from here
     #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
     #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
-    doAssert 0 == t.getOrDefault(testKey)
+    doAssert 0 == t[testKey]
     t.inc(testKey, 3)
-    doAssert 3 == t.getOrDefault(testKey)
+    doAssert 3 == t[testKey]
 
   block:
     # Clear tests
@@ -1394,6 +2777,18 @@ when isMainModule:
     let t = toCountTable([0, 0, 5, 5, 5])
     doAssert t.smallest == (0, 2)
 
+  block: #10065
+    let t = toCountTable("abracadabra")
+    doAssert t['z'] == 0
+
+    var t_mut = toCountTable("abracadabra")
+    doAssert t_mut['z'] == 0
+    # the previous read may not have modified the table.
+    doAssert t_mut.hasKey('z') == false
+    t_mut['z'] = 1
+    doAssert t_mut['z'] == 1
+    doAssert t_mut.hasKey('z') == true
+
   block:
     var tp: Table[string, string] = initTable[string, string]()
     doAssert "test1" == tp.getOrDefault("test1", "test1")
diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim
new file mode 100644
index 000000000..9e716bdf4
--- /dev/null
+++ b/lib/pure/concurrency/atomics.nim
@@ -0,0 +1,378 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Jörg Wollenschläger
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Types and operations for atomic operations and lockless algorithms.
+
+import macros
+
+when defined(cpp) or defined(nimdoc):
+  # For the C++ backend, types and operations map directly to C++11 atomics.
+
+  {.push, header: "<atomic>".}
+
+  type
+    MemoryOrder* {.importcpp: "std::memory_order".} = enum
+      ## Specifies how non-atomic operations can be reordered around atomic 
+      ## operations.
+
+      moRelaxed
+        ## No ordering constraints. Only the atomicity and ordering against
+        ## other atomic operations is guaranteed.
+
+      moConsume
+        ## This ordering is currently discouraged as it's semantics are
+        ## being revised. Acquire operations should be preferred.
+
+      moAcquire
+        ## When applied to a load operation, no reads or writes in the
+        ## current thread can be reordered before this operation.
+
+      moRelease
+        ## When applied to a store operation, no reads or writes in the
+        ## current thread can be reorderd after this operation.
+
+      moAcquireRelease
+        ## When applied to a read-modify-write operation, this behaves like
+        ## both an acquire and a release operation.
+
+      moSequentiallyConsistent
+        ## Behaves like Acquire when applied to load, like Release when
+        ## applied to a store and like AcquireRelease when applied to a
+        ## read-modify-write operation.
+        ## Also garantees that all threads observe the same total ordering
+        ## with other moSequentiallyConsistent operations.
+
+  type
+    Atomic* {.importcpp: "std::atomic".} [T] = object
+      ## An atomic object with underlying type `T`.
+
+    AtomicFlag* {.importcpp: "std::atomic_flag".} = object
+      ## An atomic boolean state.
+
+  # Access operations
+
+  proc load*[T](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.load(@)".}
+    ## Atomically obtains the value of the atomic object.
+
+  proc store*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.store(@)".}
+    ## Atomically replaces the value of the atomic object with the `desired`
+    ## value.
+
+  proc exchange*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.exchange(@)".}
+    ## Atomically replaces the value of the atomic object with the `desired`
+    ## value and returns the old value.
+
+  proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_strong(@)".}
+    ## Atomically compares the value of the atomic object with the `expected`
+    ## value and performs exchange with the `desired` one if equal or load if
+    ## not. Returns true if the exchange was successful.
+
+  proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_strong(@)".}
+    ## Same as above, but allows for different memory orders for success and
+    ## failure.
+
+  proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_weak(@)".}
+    ## Same as above, but is allowed to fail spuriously.
+
+  proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_weak(@)".}
+    ## Same as above, but allows for different memory orders for success and
+    ## failure.
+
+  # Numerical operations
+
+  proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_add(@)".}
+    ## Atomically adds a `value` to the atomic integer and returns the
+    ## original value.
+
+  proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_sub(@)".}
+    ## Atomically subtracts a `value` to the atomic integer and returns the
+    ## original value.
+
+  proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_and(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise AND
+    ## with the specified `value` and returns the original value.
+
+  proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_or(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise OR
+    ## with the specified `value` and returns the original value.
+
+  proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_xor(@)".}
+    ## Atomically replaces the atomic integer with it's bitwise XOR
+    ## with the specified `value` and returns the original value.
+
+  # Flag operations
+
+  proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.test_and_set(@)".}
+    ## Atomically sets the atomic flag to true and returns the original value.
+
+  proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.clear(@)".}
+    ## Atomically sets the value of the atomic flag to false.
+
+  proc fence*(order: MemoryOrder) {.importcpp: "std::atomic_thread_fence(@)".}
+    ## Ensures memory ordering without using atomic operations.
+
+  proc signalFence*(order: MemoryOrder) {.importcpp: "std::atomic_signal_fence(@)".}
+    ## Prevents reordering of accesses by the compiler as would fence, but
+    ## inserts no CPU instructions for memory ordering.
+
+  {.pop.}
+
+else:
+  # For the C backend, atomics map to C11 built-ins on GCC and Clang for
+  # trivial Nim types. Other types are implemented using spin locks.
+  # This could be overcome by supporting advanced importc-patterns.
+
+  # Since MSVC does not implement C11, we fall back to MS intrinsics
+  # where available.
+
+  type 
+    Trivial = SomeNumber | bool | ptr | pointer
+      # A type that is known to be atomic and whose size is known at
+      # compile time to be 8 bytes or less
+
+  template nonAtomicType(T: typedesc[Trivial]): untyped =
+    # Maps types to integers of the same size
+    when sizeof(T) == 1: int8
+    elif sizeof(T) == 2: int16
+    elif sizeof(T) == 4: int32
+    elif sizeof(T) == 8: int64
+
+  when defined(vcc):
+    
+    # TODO: Trivial types should be volatile and use VC's special volatile
+    # semantics for store and loads.
+
+    type
+      MemoryOrder* = enum
+        moRelaxed
+        moConsume
+        moAcquire
+        moRelease
+        moAcquireRelease
+        moSequentiallyConsistent
+
+      Atomic*[T] = object
+        when T is Trivial:
+          value: T.nonAtomicType
+        else:
+          nonAtomicValue: T
+          guard: AtomicFlag
+
+      AtomicFlag* = distinct int8
+
+    {.push header: "<intrin.h>".}
+
+    # MSVC intrinsics
+    proc interlockedExchange(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8".}
+    proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange".}
+    proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange16".}
+    proc interlockedExchange(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64".}
+
+    proc interlockedCompareExchange(location: pointer; desired, expected: int8): int8 {.importc: "_InterlockedCompareExchange8".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int16): int16 {.importc: "_InterlockedCompareExchange16".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int32): int32 {.importc: "_InterlockedCompareExchange".}
+    proc interlockedCompareExchange(location: pointer; desired, expected: int64): int64 {.importc: "_InterlockedCompareExchange64".}
+
+    proc interlockedAnd(location: pointer; value: int8): int8 {.importc: "_InterlockedAnd8".}
+    proc interlockedAnd(location: pointer; value: int16): int16 {.importc: "_InterlockedAnd16".}
+    proc interlockedAnd(location: pointer; value: int32): int32 {.importc: "_InterlockedAnd".}
+    proc interlockedAnd(location: pointer; value: int64): int64 {.importc: "_InterlockedAnd64".}
+
+    proc interlockedOr(location: pointer; value: int8): int8 {.importc: "_InterlockedOr8".}
+    proc interlockedOr(location: pointer; value: int16): int16 {.importc: "_InterlockedOr16".}
+    proc interlockedOr(location: pointer; value: int32): int32 {.importc: "_InterlockedOr".}
+    proc interlockedOr(location: pointer; value: int64): int64 {.importc: "_InterlockedOr64".}
+
+    proc interlockedXor(location: pointer; value: int8): int8 {.importc: "_InterlockedXor8".}
+    proc interlockedXor(location: pointer; value: int16): int16 {.importc: "_InterlockedXor16".}
+    proc interlockedXor(location: pointer; value: int32): int32 {.importc: "_InterlockedXor".}
+    proc interlockedXor(location: pointer; value: int64): int64 {.importc: "_InterlockedXor64".}
+
+    proc fence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
+    proc signalFence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
+
+    {.pop.}
+
+    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool =
+      interlockedOr(addr(location), 1'i8) == 1'i8
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) =
+      discard interlockedAnd(addr(location), 0'i8)
+
+    proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedOr(addr(location.value), (nonAtomicType(T))0))
+    proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
+      discard interlockedExchange(addr(location.value), cast[nonAtomicType(T)](desired))
+
+    proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedExchange(addr(location.value), cast[int64](desired)))
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      cast[T](interlockedCompareExchange(addr(location.value), cast[nonAtomicType(T)](desired), cast[nonAtomicType(T)](expected))) == expected
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchange(location, expected, desired, order, order)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      compareExchange(location, expected, desired, success, failure)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchangeWeak(location, expected, desired, order, order)
+
+    proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      var currentValue = location.load()
+      while not compareExchangeWeak(location, currentValue, currentValue + value): discard
+    proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      fetchAdd(location, -value, order)
+    proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedAnd(addr(location.value), cast[nonAtomicType(T)](value)))
+    proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedOr(addr(location.value), cast[nonAtomicType(T)](value)))
+    proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
+    
+  else:
+    {.push, header: "<stdatomic.h>".}
+
+    type
+      MemoryOrder* {.importc: "memory_order".} = enum
+        moRelaxed
+        moConsume
+        moAcquire
+        moRelease
+        moAcquireRelease
+        moSequentiallyConsistent
+
+    type
+      # Atomic* {.importcpp: "_Atomic('0)".} [T] = object
+
+      AtomicInt8 {.importc: "_Atomic NI8".} = object
+      AtomicInt16 {.importc: "_Atomic NI16".} = object
+      AtomicInt32 {.importc: "_Atomic NI32".} = object
+      AtomicInt64 {.importc: "_Atomic NI64".} = object
+
+    template atomicType(T: typedesc[Trivial]): untyped =
+      # Maps the size of a trivial type to it's internal atomic type
+      when sizeof(T) == 1: AtomicInt8
+      elif sizeof(T) == 2: AtomicInt16
+      elif sizeof(T) == 4: AtomicInt32
+      elif sizeof(T) == 8: AtomicInt64
+
+    type
+      AtomicFlag* {.importc: "atomic_flag".} = object
+
+      Atomic*[T] = object
+        when T is Trivial:
+          value: T.atomicType
+        else:
+          nonAtomicValue: T
+          guard: AtomicFlag
+
+    #proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
+    proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
+    proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
+    proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
+    proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
+      
+    # Numerical operations
+    proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+  
+    # Flag operations
+    # var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
+    # proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
+    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
+  
+    proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
+    proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}  
+
+    {.pop.}
+
+    proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_load_explicit[nonAtomicType(T), type(location.value)](addr(location.value), order))
+    proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
+      atomic_store_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order)
+    proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_exchange_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order))
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      atomic_compare_exchange_strong_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
+    proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchange(location, expected, desired, order, order)
+
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+      atomic_compare_exchange_weak_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
+    proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+      compareExchangeWeak(location, expected, desired, order, order)
+  
+    # Numerical operations
+    proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_add_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_sub_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_and_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_or_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+    proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+      cast[T](atomic_fetch_xor_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
+
+  template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped =
+    while location.guard.testAndSet(moAcquire): discard
+    body
+    location.guard.clear(moRelease)
+
+  proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =      
+    withLock(location, order):
+      result = location.nonAtomicValue
+
+  proc store*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =      
+    withLock(location, order):
+      location.nonAtomicValue = desired
+
+  proc exchange*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
+    withLock(location, order):
+      result = location.nonAtomicValue
+      location.nonAtomicValue = desired
+
+  proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+    withLock(location, success):
+      if location.nonAtomicValue != expected:
+        return false
+      swap(location.nonAtomicValue, expected)
+      return true
+
+  proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
+    withLock(location, success):
+      if location.nonAtomicValue != expected:
+        return false
+      swap(location.nonAtomicValue, expected)
+      return true
+
+  proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+    compareExchange(location, expected, desired, order, order)
+
+  proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
+    compareExchangeWeak(location, expected, desired, order, order)
+
+proc atomicInc*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
+  ## Atomically increments the atomic integer by some `value`.
+  discard location.fetchAdd(value)
+
+proc atomicDec*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
+  ## Atomically decrements the atomic integer by some `value`.
+  discard location.fetchSub(value)
+
+proc `+=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
+  ## Atomically increments the atomic integer by some `value`.
+  discard location.fetchAdd(value)
+
+proc `-=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
+  ## Atomically decrements the atomic integer by some `value`.
+  discard location.fetchSub(value)
+  
\ No newline at end of file
diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim
index 5847cfadb..0f1ffb1ab 100644
--- a/lib/pure/distros.nim
+++ b/lib/pure/distros.nim
@@ -133,16 +133,17 @@ const
   LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware,
     Distribution.ArchLinux}
 
-var unameRes, releaseRes: string ## we cache the result of the 'uname -a'
-                                 ## execution for faster platform detections.
+var unameRes, releaseRes, hostnamectlRes: string ## we cache the result of the 'uname -a'
+                                                 ## execution for faster platform detections.
 
 template unameRelease(cmd, cache): untyped =
   if cache.len == 0:
     cache = (when defined(nimscript): gorge(cmd) else: execProcess(cmd))
   cache
 
-template uname(): untyped = unameRelease("uname -a", unameRes)
-template release(): untyped = unameRelease("lsb_release -a", releaseRes)
+template uname(): untyped = unameRelease("uname -o", unameRes)
+template release(): untyped = unameRelease("lsb_release -d", releaseRes)
+template hostnamectl(): untyped = unameRelease("hostnamectl", hostnamectlRes)
 
 proc detectOsImpl(d: Distribution): bool =
   case d
@@ -172,7 +173,7 @@ proc detectOsImpl(d: Distribution): bool =
     result = defined(haiku)
   else:
     let dd = toLowerAscii($d)
-    result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release())
+    result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release()) or ("operating system: " & dd) in toLowerAscii(hostnamectl())
 
 template detectOs*(d: untyped): bool =
   ## Distro/OS detection. For convenience the
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index b7498b1c5..269ae476f 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -189,12 +189,6 @@ proc body*(response: Response): string =
     response.body = response.bodyStream.readAll()
   return response.body
 
-proc `body=`*(response: Response, value: string) {.deprecated.} =
-  ## Setter for backward compatibility.
-  ##
-  ## **This is deprecated and should not be used**.
-  response.body = value
-
 proc body*(response: AsyncResponse): Future[string] {.async.} =
   ## Reads the response's body and caches it. The read is performed only
   ## once.
@@ -477,119 +471,6 @@ proc format(p: MultipartData): tuple[contentType, body: string] =
     result.body.add("--" & bound & "\c\L" & s)
   result.body.add("--" & bound & "--\c\L")
 
-proc request*(url: string, httpMethod: string, extraHeaders = "",
-              body = "", sslContext = getDefaultSSL(), timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response
-              {.deprecated: "use HttpClient.request instead".} =
-  ## | Requests ``url`` with the custom method string specified by the
-  ## | ``httpMethod`` parameter.
-  ## | Extra headers can be specified and must be separated by ``\c\L``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
-  var r = if proxy == nil: parseUri(url) else: proxy.url
-  var hostUrl = if proxy == nil: r else: parseUri(url)
-  var headers = httpMethod.toUpperAscii()
-  # TODO: Use generateHeaders further down once it supports proxies.
-
-  var s = newSocket()
-  defer: s.close()
-  if s == nil: raiseOSError(osLastError())
-  var port = net.Port(80)
-  if r.scheme == "https":
-    when defined(ssl):
-      sslContext.wrapSocket(s)
-      port = net.Port(443)
-    else:
-      raise newException(HttpRequestError,
-                "SSL support is not available. Cannot connect over SSL. Compile with -d:ssl to enable.")
-  if r.port != "":
-    port = net.Port(r.port.parseInt)
-
-
-  # get the socket ready. If we are connecting through a proxy to SSL,
-  # send the appropriate CONNECT header. If not, simply connect to the proper
-  # host (which may still be the proxy, for normal HTTP)
-  if proxy != nil and hostUrl.scheme == "https":
-    when defined(ssl):
-      var connectHeaders = "CONNECT "
-      let targetPort = if hostUrl.port == "": 443 else: hostUrl.port.parseInt
-      connectHeaders.add(hostUrl.hostname)
-      connectHeaders.add(":" & $targetPort)
-      connectHeaders.add(" HTTP/1.1\c\L")
-      connectHeaders.add("Host: " & hostUrl.hostname & ":" & $targetPort & "\c\L")
-      if proxy.auth != "":
-        let auth = base64.encode(proxy.auth, newline = "")
-        connectHeaders.add("Proxy-Authorization: basic " & auth & "\c\L")
-      connectHeaders.add("\c\L")
-      if timeout == -1:
-        s.connect(r.hostname, port)
-      else:
-        s.connect(r.hostname, port, timeout)
-
-      s.send(connectHeaders)
-      let connectResult = parseResponse(s, false, timeout)
-      if not connectResult.status.startsWith("200"):
-        raise newException(HttpRequestError,
-                           "The proxy server rejected a CONNECT request, " &
-                           "so a secure connection could not be established.")
-      sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
-    else:
-      raise newException(HttpRequestError, "SSL support not available. Cannot " &
-                         "connect via proxy over SSL. Compile with -d:ssl to enable.")
-  else:
-    if timeout == -1:
-      s.connect(r.hostname, port)
-    else:
-      s.connect(r.hostname, port, timeout)
-
-
-  # now that the socket is ready, prepare the headers
-  if proxy == nil:
-    headers.add ' '
-    if r.path[0] != '/': headers.add '/'
-    headers.add(r.path)
-    if r.query.len > 0:
-      headers.add("?" & r.query)
-  else:
-    headers.add(" " & url)
-
-  headers.add(" HTTP/1.1\c\L")
-
-  if hostUrl.port == "":
-    add(headers, "Host: " & hostUrl.hostname & "\c\L")
-  else:
-    add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
-
-  if userAgent != "":
-    add(headers, "User-Agent: " & userAgent & "\c\L")
-  if proxy != nil and proxy.auth != "":
-    let auth = base64.encode(proxy.auth, newline = "")
-    add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
-  add(headers, extraHeaders)
-  add(headers, "\c\L")
-
-  # headers are ready. send them, await the result, and close the socket.
-  s.send(headers)
-  if body != "":
-    s.send(body)
-
-  result = parseResponse(s, httpMethod != "HEAD", timeout)
-
-proc request*(url: string, httpMethod = HttpGET, extraHeaders = "",
-              body = "", sslContext = getDefaultSSL(), timeout = -1,
-              userAgent = defUserAgent, proxy: Proxy = nil): Response
-              {.deprecated.} =
-  ## | Requests ``url`` with the specified ``httpMethod``.
-  ## | Extra headers can be specified and must be separated by ``\c\L``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
-  result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout,
-                   userAgent, proxy)
-
 proc redirection(status: string): bool =
   const redirectionNRs = ["301", "302", "303", "307"]
   for i in items(redirectionNRs):
@@ -608,130 +489,6 @@ proc getNewLocation(lastURL: string, headers: HttpHeaders): string =
     parsed.anchor = r.anchor
     result = $parsed
 
-proc get*(url: string, extraHeaders = "", maxRedirects = 5,
-          sslContext: SSLContext = getDefaultSSL(),
-          timeout = -1, userAgent = defUserAgent,
-          proxy: Proxy = nil): Response {.deprecated.} =
-  ## | GETs the ``url`` and returns a ``Response`` object
-  ## | This proc also handles redirection
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead.
-  result = request(url, HttpGET, extraHeaders, "", sslContext, timeout,
-                   userAgent, proxy)
-  var lastURL = url
-  for i in 1..maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      result = request(redirectTo, HttpGET, extraHeaders, "", sslContext,
-                       timeout, userAgent, proxy)
-      lastURL = redirectTo
-
-proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
-                 sslContext: SSLContext = getDefaultSSL(),
-                 timeout = -1, userAgent = defUserAgent,
-                 proxy: Proxy = nil): string {.deprecated.} =
-  ## | GETs the body and returns it as a string.
-  ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.getContent`` instead.
-  var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent,
-              proxy)
-  if r.status[0] in {'4','5'}:
-    raise newException(HttpRequestError, r.status)
-  else:
-    return r.body
-
-proc post*(url: string, extraHeaders = "", body = "",
-           maxRedirects = 5,
-           sslContext: SSLContext = getDefaultSSL(),
-           timeout = -1, userAgent = defUserAgent,
-           proxy: Proxy = nil,
-           multipart: MultipartData = nil): Response {.deprecated.} =
-  ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
-  ## | This proc adds the necessary Content-Length header.
-  ## | This proc also handles redirection.
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ## | The optional ``multipart`` parameter can be used to create
-  ## ``multipart/form-data`` POSTs comfortably.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead.
-  let (mpContentType, mpBody) = format(multipart)
-
-  template withNewLine(x): untyped =
-    if x.len > 0 and not x.endsWith("\c\L"):
-      x & "\c\L"
-    else:
-      x
-
-  var xb = mpBody.withNewLine() & body
-
-  var xh = extraHeaders.withNewLine() &
-    withNewLine("Content-Length: " & $len(xb))
-
-  if not multipart.isNil:
-    xh.add(withNewLine("Content-Type: " & mpContentType))
-
-  result = request(url, HttpPOST, xh, xb, sslContext, timeout, userAgent,
-                   proxy)
-  var lastURL = url
-  for i in 1..maxRedirects:
-    if result.status.redirection():
-      let redirectTo = getNewLocation(lastURL, result.headers)
-      var meth = if result.status != "307": HttpGet else: HttpPost
-      result = request(redirectTo, meth, xh, xb, sslContext, timeout,
-                       userAgent, proxy)
-      lastURL = redirectTo
-
-proc postContent*(url: string, extraHeaders = "", body = "",
-                  maxRedirects = 5,
-                  sslContext: SSLContext = getDefaultSSL(),
-                  timeout = -1, userAgent = defUserAgent,
-                  proxy: Proxy = nil,
-                  multipart: MultipartData = nil): string
-                  {.deprecated.} =
-  ## | POSTs ``body`` to ``url`` and returns the response's body as a string
-  ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
-  ## | Extra headers can be specified and must be separated by ``\c\L``.
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ## | The optional ``multipart`` parameter can be used to create
-  ## ``multipart/form-data`` POSTs comfortably.
-  ##
-  ## **Deprecated since version 0.15.0**: use ``HttpClient.postContent``
-  ## instead.
-  var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
-               userAgent, proxy, multipart)
-  if r.status[0] in {'4','5'}:
-    raise newException(HttpRequestError, r.status)
-  else:
-    return r.body
-
-proc downloadFile*(url: string, outputFilename: string,
-                   sslContext: SSLContext = getDefaultSSL(),
-                   timeout = -1, userAgent = defUserAgent,
-                   proxy: Proxy = nil) {.deprecated.} =
-  ## | Downloads ``url`` and saves it to ``outputFilename``
-  ## | An optional timeout can be specified in milliseconds, if reading from the
-  ## server takes longer than specified an ETimeout exception will be raised.
-  ##
-  ## **Deprecated since version 0.16.2**: use ``HttpClient.downloadFile``
-  ## instead.
-  var f: File
-  if open(f, outputFilename, fmWrite):
-    f.write(getContent(url, sslContext = sslContext, timeout = timeout,
-            userAgent = userAgent, proxy = proxy))
-    f.close()
-  else:
-    fileError("Unable to open file")
-
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
deleted file mode 100644
index a81e8c0a8..000000000
--- a/lib/pure/httpserver.nim
+++ /dev/null
@@ -1,535 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf, Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements a simple HTTP-Server.
-##
-## **Warning**: This module will soon be deprecated in favour of
-## the ``asyncdispatch`` module, you should use it instead.
-##
-## Example:
-##
-## .. code-block:: nim
-##  import strutils, sockets, httpserver
-##
-##  var counter = 0
-##  proc handleRequest(client: Socket, path, query: string): bool {.procvar.} =
-##    inc(counter)
-##    client.send("Hello for the $#th time." % $counter & wwwNL)
-##    return false # do not stop processing
-##
-##  run(handleRequest, Port(80))
-##
-
-import parseutils, strutils, os, osproc, strtabs, streams, sockets, asyncio
-
-const
-  wwwNL* = "\r\L"
-  ServerSig = "Server: httpserver.nim/1.0.0" & wwwNL
-
-# --------------- output messages --------------------------------------------
-
-proc sendTextContentType(client: Socket) =
-  send(client, "Content-type: text/html" & wwwNL)
-  send(client, wwwNL)
-
-proc sendStatus(client: Socket, status: string) =
-  send(client, "HTTP/1.1 " & status & wwwNL)
-
-proc badRequest(client: Socket) =
-  # Inform the client that a request it has made has a problem.
-  send(client, "HTTP/1.1 400 Bad Request" & wwwNL)
-  sendTextContentType(client)
-  send(client, "<p>Your browser sent a bad request, " &
-               "such as a POST without a Content-Length.</p>" & wwwNL)
-
-when false:
-  proc cannotExec(client: Socket) =
-    send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
-    sendTextContentType(client)
-    send(client, "<P>Error prohibited CGI execution." & wwwNL)
-
-proc headers(client: Socket, filename: string) =
-  # XXX could use filename to determine file type
-  send(client, "HTTP/1.1 200 OK" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-
-proc notFound(client: Socket) =
-  send(client, "HTTP/1.1 404 NOT FOUND" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-  send(client, "<html><title>Not Found</title>" & wwwNL)
-  send(client, "<body><p>The server could not fulfill" & wwwNL)
-  send(client, "your request because the resource specified" & wwwNL)
-  send(client, "is unavailable or nonexistent.</p>" & wwwNL)
-  send(client, "</body></html>" & wwwNL)
-
-proc unimplemented(client: Socket) =
-  send(client, "HTTP/1.1 501 Method Not Implemented" & wwwNL)
-  send(client, ServerSig)
-  sendTextContentType(client)
-  send(client, "<html><head><title>Method Not Implemented" &
-               "</title></head>" &
-               "<body><p>HTTP request method not supported.</p>" &
-               "</body></HTML>" & wwwNL)
-
-# ----------------- file serving ---------------------------------------------
-
-when false:
-  proc discardHeaders(client: Socket) = skip(client)
-
-proc serveFile*(client: Socket, filename: string) =
-  ## serves a file to the client.
-  var f: File
-  if open(f, filename):
-    headers(client, filename)
-    const bufSize = 8000 # != 8K might be good for memory manager
-    var buf = alloc(bufsize)
-    while true:
-      var bytesread = readBuffer(f, buf, bufsize)
-      if bytesread > 0:
-        var byteswritten = send(client, buf, bytesread)
-        if bytesread != bytesWritten:
-          dealloc(buf)
-          close(f)
-          raiseOSError(osLastError())
-      if bytesread != bufSize: break
-    dealloc(buf)
-    close(f)
-  else:
-    notFound(client)
-
-# ------------------ CGI execution -------------------------------------------
-when false:
-  # TODO: Fix this, or get rid of it.
-  type
-    RequestMethod = enum reqGet, reqPost
-
-  proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
-    var env = newStringTable(modeCaseInsensitive)
-    var contentLength = -1
-    case meth
-    of reqGet:
-      discardHeaders(client)
-
-      env["REQUEST_METHOD"] = "GET"
-      env["QUERY_STRING"] = query
-    of reqPost:
-      var buf = TaintedString""
-      var dataAvail = false
-      while dataAvail:
-        dataAvail = recvLine(client, buf) # TODO: This is incorrect.
-        var L = toLowerAscii(buf.string)
-        if L.startsWith("content-length:"):
-          var i = len("content-length:")
-          while L[i] in Whitespace: inc(i)
-          contentLength = parseInt(substr(L, i))
-
-      if contentLength < 0:
-        badRequest(client)
-        return
-
-      env["REQUEST_METHOD"] = "POST"
-      env["CONTENT_LENGTH"] = $contentLength
-
-    send(client, "HTTP/1.0 200 OK" & wwwNL)
-
-    var process = startProcess(command=path, env=env)
-    if meth == reqPost:
-      # get from client and post to CGI program:
-      var buf = alloc(contentLength)
-      if recv(client, buf, contentLength) != contentLength:
-        dealloc(buf)
-        raiseOSError()
-      var inp = process.inputStream
-      inp.writeData(buf, contentLength)
-      dealloc(buf)
-
-    var outp = process.outputStream
-    var line = newStringOfCap(120).TaintedString
-    while true:
-      if outp.readLine(line):
-        send(client, line.string)
-        send(client, wwwNL)
-      elif not running(process): break
-
-  # --------------- Server Setup -----------------------------------------------
-
-  proc acceptRequest(client: Socket) =
-    var cgi = false
-    var query = ""
-    var buf = TaintedString""
-    discard recvLine(client, buf)
-    var path = ""
-    var data = buf.string.split()
-    var meth = reqGet
-
-    var q = find(data[1], '?')
-
-    # extract path
-    if q >= 0:
-      # strip "?..." from path, this may be found in both POST and GET
-      path = "." & data[1].substr(0, q-1)
-    else:
-      path = "." & data[1]
-    # path starts with "/", by adding "." in front of it we serve files from cwd
-
-    if cmpIgnoreCase(data[0], "GET") == 0:
-      if q >= 0:
-        cgi = true
-        query = data[1].substr(q+1)
-    elif cmpIgnoreCase(data[0], "POST") == 0:
-      cgi = true
-      meth = reqPost
-    else:
-      unimplemented(client)
-
-    if path[path.len-1] == '/' or existsDir(path):
-      path = path / "index.html"
-
-    if not existsFile(path):
-      discardHeaders(client)
-      notFound(client)
-    else:
-      when defined(Windows):
-        var ext = splitFile(path).ext.toLowerAscii
-        if ext == ".exe" or ext == ".cgi":
-          # XXX: extract interpreter information here?
-          cgi = true
-      else:
-        if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}:
-          cgi = true
-      if not cgi:
-        serveFile(client, path)
-      else:
-        executeCgi(client, path, query, meth)
-
-type
-  Server* = object of RootObj  ## contains the current server state
-    socket: Socket
-    port: Port
-    client*: Socket          ## the socket to write the file data to
-    reqMethod*: string       ## Request method. GET or POST.
-    path*, query*: string    ## path and query the client requested
-    headers*: StringTableRef ## headers with which the client made the request
-    body*: string            ## only set with POST requests
-    ip*: string              ## ip address of the requesting client
-
-  PAsyncHTTPServer* = ref AsyncHTTPServer
-  AsyncHTTPServer = object of Server
-    asyncSocket: AsyncSocket
-
-proc open*(s: var Server, port = Port(80), reuseAddr = false) =
-  ## creates a new server at port `port`. If ``port == 0`` a free port is
-  ## acquired that can be accessed later by the ``port`` proc.
-  s.socket = socket(AF_INET)
-  if s.socket == invalidSocket: raiseOSError(osLastError())
-  if reuseAddr:
-    s.socket.setSockOpt(OptReuseAddr, true)
-  bindAddr(s.socket, port)
-  listen(s.socket)
-
-  if port == Port(0):
-    s.port = getSockName(s.socket)
-  else:
-    s.port = port
-  s.client = invalidSocket
-  s.reqMethod = ""
-  s.body = ""
-  s.path = ""
-  s.query = ""
-  s.headers = {:}.newStringTable()
-
-proc port*(s: var Server): Port =
-  ## get the port number the server has acquired.
-  result = s.port
-
-proc next*(s: var Server) =
-  ## proceed to the first/next request.
-  var client: Socket
-  new(client)
-  var ip: string
-  acceptAddr(s.socket, client, ip)
-  s.client = client
-  s.ip = ip
-  s.headers = newStringTable(modeCaseInsensitive)
-  #headers(s.client, "")
-  var data = ""
-  s.client.readLine(data)
-  if data == "":
-    # Socket disconnected
-    s.client.close()
-    next(s)
-    return
-  var header = ""
-  while true:
-    s.client.readLine(header)
-    if header == "\c\L": break
-    if header != "":
-      var i = 0
-      var key = ""
-      var value = ""
-      i = header.parseUntil(key, ':')
-      inc(i) # skip :
-      i += header.skipWhiteSpace(i)
-      i += header.parseUntil(value, {'\c', '\L'}, i)
-      s.headers[key] = value
-    else:
-      s.client.close()
-      next(s)
-      return
-
-  var i = skipWhitespace(data)
-  if skipIgnoreCase(data, "GET") > 0:
-    s.reqMethod = "GET"
-    inc(i, 3)
-  elif skipIgnoreCase(data, "POST") > 0:
-    s.reqMethod = "POST"
-    inc(i, 4)
-  else:
-    unimplemented(s.client)
-    s.client.close()
-    next(s)
-    return
-
-  if s.reqMethod == "POST":
-    # Check for Expect header
-    if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLowerAscii == "100-continue":
-        s.client.sendStatus("100 Continue")
-      else:
-        s.client.sendStatus("417 Expectation Failed")
-
-    # Read the body
-    # - Check for Content-length header
-    if s.headers.hasKey("Content-Length"):
-      var contentLength = 0
-      if parseInt(s.headers["Content-Length"], contentLength) == 0:
-        badRequest(s.client)
-        s.client.close()
-        next(s)
-        return
-      else:
-        var totalRead = 0
-        var totalBody = ""
-        while totalRead < contentLength:
-          var chunkSize = 8000
-          if (contentLength - totalRead) < 8000:
-            chunkSize = (contentLength - totalRead)
-          var bodyData = newString(chunkSize)
-          var octetsRead = s.client.recv(cstring(bodyData), chunkSize)
-          if octetsRead <= 0:
-            s.client.close()
-            next(s)
-            return
-          totalRead += octetsRead
-          totalBody.add(bodyData)
-        if totalBody.len != contentLength:
-          s.client.close()
-          next(s)
-          return
-
-        s.body = totalBody
-    else:
-      badRequest(s.client)
-      s.client.close()
-      next(s)
-      return
-
-  var L = skipWhitespace(data, i)
-  inc(i, L)
-  # XXX we ignore "HTTP/1.1" etc. for now here
-  var query = 0
-  var last = i
-  while last < data.len and data[last] notin Whitespace:
-    if data[last] == '?' and query == 0: query = last
-    inc(last)
-  if query > 0:
-    s.query = data.substr(query+1, last-1)
-    s.path = data.substr(i, query-1)
-  else:
-    s.query = ""
-    s.path = data.substr(i, last-1)
-
-proc close*(s: Server) =
-  ## closes the server (and the socket the server uses).
-  close(s.socket)
-
-proc run*(handleRequest: proc (client: Socket,
-                               path, query: string): bool {.closure.},
-          port = Port(80)) =
-  ## encapsulates the server object and main loop
-  var s: Server
-  open(s, port, reuseAddr = true)
-  #echo("httpserver running on port ", s.port)
-  while true:
-    next(s)
-    if handleRequest(s.client, s.path, s.query): break
-    close(s.client)
-  close(s)
-
-# -- AsyncIO begin
-
-proc nextAsync(s: PAsyncHTTPServer) =
-  ## proceed to the first/next request.
-  var client: Socket
-  new(client)
-  var ip: string
-  acceptAddr(getSocket(s.asyncSocket), client, ip)
-  s.client = client
-  s.ip = ip
-  s.headers = newStringTable(modeCaseInsensitive)
-  #headers(s.client, "")
-  var data = ""
-  s.client.readLine(data)
-  if data == "":
-    # Socket disconnected
-    s.client.close()
-    return
-  var header = ""
-  while true:
-    s.client.readLine(header) # TODO: Very inefficient here. Prone to DOS.
-    if header == "\c\L": break
-    if header != "":
-      var i = 0
-      var key = ""
-      var value = ""
-      i = header.parseUntil(key, ':')
-      inc(i) # skip :
-      if i < header.len:
-        i += header.skipWhiteSpace(i)
-        i += header.parseUntil(value, {'\c', '\L'}, i)
-      s.headers[key] = value
-    else:
-      s.client.close()
-      return
-
-  var i = skipWhitespace(data)
-  if skipIgnoreCase(data, "GET") > 0:
-    s.reqMethod = "GET"
-    inc(i, 3)
-  elif skipIgnoreCase(data, "POST") > 0:
-    s.reqMethod = "POST"
-    inc(i, 4)
-  else:
-    unimplemented(s.client)
-    s.client.close()
-    return
-
-  if s.reqMethod == "POST":
-    # Check for Expect header
-    if s.headers.hasKey("Expect"):
-      if s.headers["Expect"].toLowerAscii == "100-continue":
-        s.client.sendStatus("100 Continue")
-      else:
-        s.client.sendStatus("417 Expectation Failed")
-
-    # Read the body
-    # - Check for Content-length header
-    if s.headers.hasKey("Content-Length"):
-      var contentLength = 0
-      if parseInt(s.headers["Content-Length"], contentLength) == 0:
-        badRequest(s.client)
-        s.client.close()
-        return
-      else:
-        var totalRead = 0
-        var totalBody = ""
-        while totalRead < contentLength:
-          var chunkSize = 8000
-          if (contentLength - totalRead) < 8000:
-            chunkSize = (contentLength - totalRead)
-          var bodyData = newString(chunkSize)
-          var octetsRead = s.client.recv(cstring(bodyData), chunkSize)
-          if octetsRead <= 0:
-            s.client.close()
-            return
-          totalRead += octetsRead
-          totalBody.add(bodyData)
-        if totalBody.len != contentLength:
-          s.client.close()
-          return
-
-        s.body = totalBody
-    else:
-      badRequest(s.client)
-      s.client.close()
-      return
-
-  var L = skipWhitespace(data, i)
-  inc(i, L)
-  # XXX we ignore "HTTP/1.1" etc. for now here
-  var query = 0
-  var last = i
-  while last < data.len and data[last] notin Whitespace:
-    if data[last] == '?' and query == 0: query = last
-    inc(last)
-  if query > 0:
-    s.query = data.substr(query+1, last-1)
-    s.path = data.substr(i, query-1)
-  else:
-    s.query = ""
-    s.path = data.substr(i, last-1)
-
-proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: Socket,
-                        path, query: string): bool {.closure, gcsafe.},
-                     port = Port(80), address = "",
-                     reuseAddr = false): PAsyncHTTPServer =
-  ## Creates an Asynchronous HTTP server at ``port``.
-  var capturedRet: PAsyncHTTPServer
-  new(capturedRet)
-  capturedRet.asyncSocket = asyncSocket()
-  capturedRet.asyncSocket.handleAccept =
-    proc (s: AsyncSocket) =
-      nextAsync(capturedRet)
-      let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path,
-                               capturedRet.query)
-      if quit: capturedRet.asyncSocket.close()
-  if reuseAddr:
-    capturedRet.asyncSocket.setSockOpt(OptReuseAddr, true)
-
-  capturedRet.asyncSocket.bindAddr(port, address)
-  capturedRet.asyncSocket.listen()
-  if port == Port(0):
-    capturedRet.port = getSockName(capturedRet.asyncSocket)
-  else:
-    capturedRet.port = port
-
-  capturedRet.client = invalidSocket
-  capturedRet.reqMethod = ""
-  capturedRet.body = ""
-  capturedRet.path = ""
-  capturedRet.query = ""
-  capturedRet.headers = {:}.newStringTable()
-  result = capturedRet
-
-proc register*(d: Dispatcher, s: PAsyncHTTPServer) =
-  ## Registers a ``PAsyncHTTPServer`` with a ``Dispatcher``.
-  d.register(s.asyncSocket)
-
-proc close*(h: PAsyncHTTPServer) =
-  ## Closes the ``PAsyncHTTPServer``.
-  h.asyncSocket.close()
-
-when not defined(testing) and isMainModule:
-  var counter = 0
-
-  var s: Server
-  open(s, Port(0))
-  echo("httpserver running on port ", s.port)
-  while true:
-    next(s)
-
-    inc(counter)
-    s.client.send("Hello, Andreas, for the $#th time. $# ? $#" % [
-      $counter, s.path, s.query] & wwwNL)
-
-    close(s.client)
-  close(s)
-
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index 4acc36b93..945555540 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -1,4 +1,4 @@
-## Include file that implements 'getEnv' and friends. Do not import it!
+# Include file that implements 'getEnv' and friends. Do not import it!
 
 when not declared(os):
   {.error: "This is an include file for os.nim!".}
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index abd0bf501..db7d84c1e 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -1,4 +1,4 @@
-## Include file that implements 'osErrorMsg' and friends. Do not import it!
+# Include file that implements 'osErrorMsg' and friends. Do not import it!
 
 when not declared(os):
   {.error: "This is an include file for os.nim!".}
@@ -59,7 +59,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
   e.errorCode = errorCode.int32
   e.msg = osErrorMsg(errorCode)
   if additionalInfo.len > 0:
-    if e.msg[^1] != '\n': e.msg.add '\n'
+    if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n'
     e.msg.add  "Additional info: "
     e.msg.addQuoted additionalInfo
   if e.msg == "":
diff --git a/lib/pure/includes/osseps.nim b/lib/pure/includes/osseps.nim
index 9a79fe303..944ad123e 100644
--- a/lib/pure/includes/osseps.nim
+++ b/lib/pure/includes/osseps.nim
@@ -85,9 +85,9 @@ elif doslikeFileSystem:
   const
     CurDir* = '.'
     ParDir* = ".."
-    DirSep* = '\\' # seperator within paths
+    DirSep* = '\\' # separator within paths
     AltSep* = '/'
-    PathSep* = ';' # seperator between paths
+    PathSep* = ';' # separator between paths
     FileSystemCaseSensitive* = false
     ExeExt* = "exe"
     ScriptExt* = "bat"
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 16d901ff0..ffd60120e 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -53,6 +53,7 @@ when hasThreadSupport:
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: ptr SharedArray[SelectorKey[T]]
       count: int
     Selector*[T] = ptr SelectorImpl[T]
@@ -61,6 +62,7 @@ else:
     SelectorImpl[T] = object
       epollFD: cint
       maxFD: int
+      numFD: int
       fds: seq[SelectorKey[T]]
       count: int
     Selector*[T] = ref SelectorImpl[T]
@@ -76,6 +78,8 @@ proc newSelector*[T](): Selector[T] =
     raiseOsError(osLastError())
   var maxFD = int(a.rlim_max)
   doAssert(maxFD > 0)
+  # Start with a reasonable size, checkFd() will grow this on demand
+  const numFD = 1024
 
   var epollFD = epoll_create(MAX_EPOLL_EVENTS)
   if epollFD < 0:
@@ -85,14 +89,16 @@ proc newSelector*[T](): Selector[T] =
     result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T])))
     result.epollFD = epollFD
     result.maxFD = maxFD
-    result.fds = allocSharedArray[SelectorKey[T]](maxFD)
+    result.numFD = numFD
+    result.fds = allocSharedArray[SelectorKey[T]](numFD)
   else:
     result = Selector[T]()
     result.epollFD = epollFD
     result.maxFD = maxFD
-    result.fds = newSeq[SelectorKey[T]](maxFD)
+    result.numFD = numFD
+    result.fds = newSeq[SelectorKey[T]](numFD)
 
-  for i in 0 ..< maxFD:
+  for i in 0 ..< numFD:
     result.fds[i].ident = InvalidIdent
 
 proc close*[T](s: Selector[T]) =
@@ -127,6 +133,16 @@ template checkFd(s, f) =
   # FD if there is too many. -- DP
   if f >= s.maxFD:
     raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
+  if f >= s.numFD:
+    var numFD = s.numFD
+    while numFD <= f: numFD *= 2
+    when hasThreadSupport:
+      s.fds = reallocSharedArray(s.fds, numFD)
+    else:
+      s.fds.setLen(numFD)
+    for i in s.numFD ..< numFD:
+      s.fds[i].ident = InvalidIdent
+    s.numFD = numFD
 
 proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 389df4087..cf979e2d0 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -134,8 +134,16 @@
 ##    j2["details"] = %* {"age":35, "pi":3.1415}
 ##    echo j2
 
+runnableExamples:
+  ## Note: for JObject, key ordering is preserved, unlike in some languages,
+  ## this is convenient for some use cases. Example:
+  type Foo = object
+    a1, a2, a0, a3, a4: int
+  doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
+
 import
-  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson
+  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson,
+  typetraits
 
 export
   tables.`$`
@@ -349,6 +357,25 @@ when false:
     assert false notin elements, "usage error: only empty sets allowed"
     assert true notin elements, "usage error: only empty sets allowed"
 
+proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
+  ## Sets a field from a `JObject`.
+  assert(obj.kind == JObject)
+  obj.fields[key] = val
+
+#[
+Note: could use simply:
+proc `%`*(o: object|tuple): JsonNode
+but blocked by https://github.com/nim-lang/Nim/issues/10019
+]#
+proc `%`*(o: tuple): JsonNode =
+  ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
+  when isNamedTuple(type(o)):
+    result = newJObject()
+    for k, v in o.fieldPairs: result[k] = %v
+  else:
+    result = newJArray()
+    for a in o.fields: result.add(%a)
+
 proc `%`*(o: object): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
   result = newJObject()
@@ -500,11 +527,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
 proc existsKey*(node: JsonNode, key: string): bool {.deprecated: "use hasKey instead".} = node.hasKey(key)
   ## **Deprecated:** use `hasKey` instead.
 
-proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
-  ## Sets a field from a `JObject`.
-  assert(obj.kind == JObject)
-  obj.fields[key] = val
-
 proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode =
   ## Traverses the node and gets the given value. If any of the
   ## keys do not exist, returns ``nil``. Also returns ``nil`` if one of the
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index cd13deec3..febd0b602 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -12,7 +12,7 @@
 ## write your own.
 ##
 ## Format strings support the following variables which must be prefixed with
-## the dollar operator (``$``):
+## the dollar operator (``$``, see example below):
 ##
 ## ============  =======================
 ##   Operator     Output
@@ -43,6 +43,11 @@
 ##    warn("4 8 15 16 23 4-- Error")
 ##    error("922044:16 SYSTEM FAILURE")
 ##    fatal("SYSTEM FAILURE SYSTEM FAILURE")
+##    # Using the aformetioned operator
+##    var opL = newConsoleLogger(fmtStr = "$datetime :: ")
+##    addHandler(opL)
+##    info("Starting web server...")
+##    # Will print something like 2018-12-17T19:28:05 :: Starting web server...
 ##
 ## **Warning:** The global list of handlers is a thread var, this means that
 ## the handlers must be re-added in each thread.
diff --git a/lib/pure/matchers.nim b/lib/pure/matchers.nim
deleted file mode 100644
index 97223ed01..000000000
--- a/lib/pure/matchers.nim
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module contains various string matchers for email addresses, etc.
-##
-## **Warning:** This module is deprecated since version 0.14.0.
-{.deprecated.}
-
-{.deadCodeElim: on.}  # dce option deprecated
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
-
-include "system/inclrtl"
-
-import parseutils, strutils
-
-proc validEmailAddress*(s: string): bool {.noSideEffect,
-  rtl, extern: "nsuValidEmailAddress".} =
-  ## returns true if `s` seems to be a valid e-mail address.
-  ## The checking also uses a domain list.
-  const
-    chars = Letters + Digits + {'!','#','$','%','&',
-      '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
-  var i = 0
-  if i >= s.len or s[i] notin chars or s[i] == '.': return false
-  while i < s.len and s[i] in chars:
-    if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
-    inc(i)
-  if i >= s.len or s[i] != '@': return false
-  var j = len(s)-1
-  if j >= 0 and s[j] notin Letters: return false
-  while j >= i and s[j] in Letters: dec(j)
-  inc(i) # skip '@'
-  while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
-  if i != s.len: return false
-
-  var x = substr(s, j+1)
-  if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
-  case toLowerAscii(x)
-  of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
-     "aero", "jobs", "museum": return true
-  else: return false
-
-proc parseInt*(s: string, value: var int, validRange: HSlice[int, int]) {.
-  noSideEffect, rtl, extern: "nmatchParseInt".} =
-  ## parses `s` into an integer in the range `validRange`. If successful,
-  ## `value` is modified to contain the result. Otherwise no exception is
-  ## raised and `value` is not touched; this way a reasonable default value
-  ## won't be overwritten.
-  var x = value
-  try:
-    discard parseutils.parseInt(s, x, 0)
-  except OverflowError:
-    discard
-  if x in validRange: value = x
-
-when isMainModule:
-  doAssert "wuseldusel@codehome.com".validEmailAddress
-
-{.pop.}
-
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index ee32772b1..460be1cd0 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -7,15 +7,48 @@
 #    distribution, for details about the copyright.
 #
 
-##   Constructive mathematics is naturally typed. -- Simon Thompson
+## *Constructive mathematics is naturally typed.* -- Simon Thompson
 ##
 ## Basic math routines for Nim.
+##
+## Note that the trigonometric functions naturally operate on radians.
+## The helper functions `degToRad<#degToRad,T>`_ and `radToDeg<#radToDeg,T>`_
+## provide conversion between radians and degrees.
+##
+## .. code-block::
+##
+##   import math
+##   from sequtils import map
+##
+##   let a = [0.0, PI/6, PI/4, PI/3, PI/2]
+##
+##   echo a.map(sin)
+##   # @[0.0, 0.499…, 0.707…, 0.866…, 1.0]
+##
+##   echo a.map(tan)
+##   # @[0.0, 0.577…, 0.999…, 1.732…, 1.633…e+16]
+##
+##   echo cos(degToRad(180.0))
+##   # -1.0
+##
+##   echo sqrt(-1.0)
+##   # nan   (use `complex` module)
+##
 ## This module is available for the `JavaScript target
 ## <backends.html#the-javascript-target>`_.
 ##
-## Note that the trigonometric functions naturally operate on radians.
-## The helper functions `degToRad` and `radToDeg` provide conversion
-## between radians and degrees.
+## **See also:**
+## * `complex module<complex.html>`_ for complex numbers and their
+##   mathematical operations
+## * `rationals module<rationals.html>`_ for rational numbers and their
+##   mathematical operations
+## * `fenv module<fenv.html>`_ for handling of floating-point rounding
+##   and exceptions (overflow, zero-devide, etc.)
+## * `random module<random.html>`_ for fast and tiny random number generator
+## * `mersenne module<mersenne.html>`_ for Mersenne twister random number generator
+## * `stats module<stats.html>`_ for statistical analysis
+## * `strformat module<strformat>`_ for formatting floats for print
+
 
 include "system/inclrtl"
 {.push debugger:off .} # the user does not want to trace a part
@@ -25,9 +58,11 @@ import bitops
 
 proc binom*(n, k: int): int {.noSideEffect.} =
   ## Computes the `binomial coefficient <https://en.wikipedia.org/wiki/Binomial_coefficient>`_.
-  ##
-  ## .. code-block:: nim
-  ##  echo binom(6, 2) ## 15
+  runnableExamples:
+    doAssert binom(6, 2) == binom(6, 4)
+    doAssert binom(6, 2) == 15
+    doAssert binom(-6, 2) == 1
+    doAssert binom(6, 0) == 1
   if k <= 0: return 1
   if 2*k > n: return binom(n, n-k)
   result = n
@@ -40,10 +75,15 @@ proc createFactTable[N: static[int]]: array[N, int] =
     result[i] = result[i - 1] * i
 
 proc fac*(n: int): int =
-  ## Computes the `factorial <https://en.wikipedia.org/wiki/Factorial>`_ of a non-negative integer ``n``
+  ## Computes the `factorial <https://en.wikipedia.org/wiki/Factorial>`_ of
+  ## a non-negative integer ``n``.
   ##
-  ## .. code-block:: nim
-  ##  echo fac(4) ## 24
+  ## See also:
+  ## * `prod proc <#prod,openArray[T]>`_
+  runnableExamples:
+    doAssert fac(3) == 6
+    doAssert fac(4) == 24
+    doAssert fac(10) == 3628800
   const factTable =
     when sizeof(int) == 4:
       createFactTable[13]()
@@ -59,25 +99,26 @@ when defined(Posix):
   {.passl: "-lm".}
 
 const
-  PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
-  TAU* = 2.0 * PI ## the circle constant TAU (= 2 * PI)
+  PI* = 3.1415926535897932384626433 ## The circle constant PI (Ludolph's number)
+  TAU* = 2.0 * PI ## The circle constant TAU (= 2 * PI)
   E* = 2.71828182845904523536028747 ## Euler's number
 
-  MaxFloat64Precision* = 16 ## maximum number of meaningful digits
+  MaxFloat64Precision* = 16 ## Maximum number of meaningful digits
                             ## after the decimal point for Nim's
                             ## ``float64`` type.
-  MaxFloat32Precision* = 8  ## maximum number of meaningful digits
+  MaxFloat32Precision* = 8  ## Maximum number of meaningful digits
                             ## after the decimal point for Nim's
                             ## ``float32`` type.
-  MaxFloatPrecision* = MaxFloat64Precision ## maximum number of
+  MaxFloatPrecision* = MaxFloat64Precision ## Maximum number of
                                            ## meaningful digits
                                            ## after the decimal point
                                            ## for Nim's ``float`` type.
-  RadPerDeg = PI / 180.0 ## number of radians per degree
+  RadPerDeg = PI / 180.0 ## Number of radians per degree
 
 type
-  FloatClass* = enum ## describes the class a floating point value belongs to.
-                     ## This is the type that is returned by `classify`.
+  FloatClass* = enum ## Describes the class a floating point value belongs to.
+                     ## This is the type that is returned by
+                     ## `classify proc <#classify,float>`_.
     fcNormal,    ## value is an ordinary nonzero floating point value
     fcSubnormal, ## value is a subnormal (a very small) floating point value
     fcZero,      ## value is zero
@@ -87,13 +128,14 @@ type
     fcNegInf     ## value is negative infinity
 
 proc classify*(x: float): FloatClass =
-  ## Classifies a floating point value. Returns ``x``'s class as specified by
-  ## `FloatClass`.
+  ## Classifies a floating point value.
   ##
-  ## .. code-block:: nim
-  ##  echo classify(0.3) ## fcNormal
-  ##  echo classify(0.0) ## fcZero
-  ##  echo classify(0.3/0.0) ## fcInf
+  ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_.
+  runnableExamples:
+    doAssert classify(0.3) == fcNormal
+    doAssert classify(0.0) == fcZero
+    doAssert classify(0.3/0.0) == fcInf
+    doAssert classify(-0.3/0.0) == fcNegInf
 
   # JavaScript and most C compilers have no classify:
   if x == 0.0:
@@ -110,20 +152,30 @@ proc classify*(x: float): FloatClass =
 
 proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
   ## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise.
+  ##
   ## Zero and negative numbers are not a power of two.
   ##
-  ## .. code-block:: nim
-  ##  echo isPowerOfTwo(5) ## false
-  ##  echo isPowerOfTwo(8) ## true
+  ## See also:
+  ## * `nextPowerOfTwo proc<#nextPowerOfTwo,int>`_
+  runnableExamples:
+    doAssert isPowerOfTwo(16) == true
+    doAssert isPowerOfTwo(5) == false
+    doAssert isPowerOfTwo(0) == false
+    doAssert isPowerOfTwo(-16) == false
   return (x > 0) and ((x and (x - 1)) == 0)
 
 proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
   ## Returns ``x`` rounded up to the nearest power of two.
+  ##
   ## Zero and negative numbers get rounded up to 1.
   ##
-  ## .. code-block:: nim
-  ##  echo nextPowerOfTwo(8) ## 8
-  ##  echo nextPowerOfTwo(9) ## 16
+  ## See also:
+  ## * `isPowerOfTwo proc<#isPowerOfTwo,int>`_
+  runnableExamples:
+    doAssert nextPowerOfTwo(16) == 16
+    doAssert nextPowerOfTwo(5) == 8
+    doAssert nextPowerOfTwo(0) == 1
+    doAssert nextPowerOfTwo(-16) == 1
   result = x - 1
   when defined(cpu64):
     result = result or (result shr 32)
@@ -138,9 +190,12 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} =
 
 proc countBits32*(n: int32): int {.noSideEffect.} =
   ## Counts the set bits in ``n``.
-  ##
-  ## .. code-block:: nim
-  ##  echo countBits32(13'i32) ## 3
+  runnableExamples:
+    doAssert countBits32(7) == 3
+    doAssert countBits32(8) == 1
+    doAssert countBits32(15) == 4
+    doAssert countBits32(16) == 1
+    doAssert countBits32(17) == 2
   var v = n
   v = v -% ((v shr 1'i32) and 0x55555555'i32)
   v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
@@ -148,41 +203,99 @@ proc countBits32*(n: int32): int {.noSideEffect.} =
 
 proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
   ## Computes the sum of the elements in ``x``.
+  ##
   ## If ``x`` is empty, 0 is returned.
   ##
-  ## .. code-block:: nim
-  ##  echo sum([1.0, 2.5, -3.0, 4.3]) ## 4.8
+  ## See also:
+  ## * `prod proc <#prod,openArray[T]>`_
+  ## * `cumsum proc <#cumsum,openArray[T]>`_
+  ## * `cumsummed proc <#cumsummed,openArray[T]>`_
+  runnableExamples:
+    doAssert sum([1, 2, 3, 4]) == 10
+    doAssert sum([-1.5, 2.7, -0.1]) == 1.1
   for i in items(x): result = result + i
 
 proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
   ## Computes the product of the elements in ``x``.
+  ##
   ## If ``x`` is empty, 1 is returned.
   ##
-  ## .. code-block:: nim
-  ##  echo prod([1.0, 3.0, -0.2]) ## -0.6
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `fac proc <#fac,int>`_
+  runnableExamples:
+    doAssert prod([1, 2, 3, 4]) == 24
+    doAssert prod([-4, 3, 5]) == -60
   result = 1.T
   for i in items(x): result = result * i
 
+proc cumsummed*[T](x: openArray[T]): seq[T] =
+  ## Return cumulative (aka prefix) summation of ``x``.
+  ##
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `cumsum proc <#cumsum,openArray[T]>`_ for the in-place version
+  runnableExamples:
+    let a = [1, 2, 3, 4]
+    doAssert cumsummed(a) == @[1, 3, 6, 10]
+  result.setLen(x.len)
+  result[0] = x[0]
+  for i in 1 ..< x.len: result[i] = result[i-1] + x[i]
+
+proc cumsum*[T](x: var openArray[T]) =
+  ## Transforms ``x`` in-place (must be declared as `var`) into its
+  ## cumulative (aka prefix) summation.
+  ##
+  ## See also:
+  ## * `sum proc <#sum,openArray[T]>`_
+  ## * `cumsummed proc <#cumsummed,openArray[T]>`_ for a version which
+  ##   returns cumsummed sequence
+  runnableExamples:
+    var a = [1, 2, 3, 4]
+    cumsum(a)
+    doAssert a == @[1, 3, 6, 10]
+  for i in 1 ..< x.len: x[i] = x[i-1] + x[i]
+
 {.push noSideEffect.}
 when not defined(JS): # C
   proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
   proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "<math.h>".}
     ## Computes the square root of ``x``.
     ##
+    ## See also:
+    ## * `cbrt proc <#cbrt,float64>`_ for cubic root
+    ##
     ## .. code-block:: nim
+    ##  echo sqrt(4.0)  ## 2.0
     ##  echo sqrt(1.44) ## 1.2
+    ##  echo sqrt(-4.0) ## nan
   proc cbrt*(x: float32): float32 {.importc: "cbrtf", header: "<math.h>".}
   proc cbrt*(x: float64): float64 {.importc: "cbrt", header: "<math.h>".}
     ## Computes the cubic root of ``x``.
     ##
+    ## See also:
+    ## * `sqrt proc <#sqrt,float64>`_ for square root
+    ##
     ## .. code-block:: nim
+    ##  echo cbrt(8.0)   ## 2.0
     ##  echo cbrt(2.197) ## 1.3
+    ##  echo cbrt(-27.0) ## -3.0
   proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
   proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
-    ## Computes the `natural logarithm <https://en.wikipedia.org/wiki/Natural_logarithm>`_ of ``x``.
+    ## Computes the `natural logarithm <https://en.wikipedia.org/wiki/Natural_logarithm>`_
+    ## of ``x``.
+    ##
+    ## See also:
+    ## * `log proc <#log,T,T>`_
+    ## * `log10 proc <#log10,float64>`_
+    ## * `log2 proc <#log2,float64>`_
+    ## * `exp proc <#exp,float64>`_
     ##
     ## .. code-block:: nim
     ##  echo ln(exp(4.0)) ## 4.0
+    ##  echo ln(1.0))     ## 0.0
+    ##  echo ln(0.0)      ## -inf
+    ##  echo ln(-7.0)     ## nan
 else: # JS
   proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
   proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
@@ -193,8 +306,18 @@ else: # JS
 proc log*[T: SomeFloat](x, base: T): T =
   ## Computes the logarithm of ``x`` to base ``base``.
   ##
+  ## See also:
+  ## * `ln proc <#ln,float64>`_
+  ## * `log10 proc <#log10,float64>`_
+  ## * `log2 proc <#log2,float64>`_
+  ## * `exp proc <#exp,float64>`_
+  ##
   ## .. code-block:: nim
-  ##  echo log(9.0, 3.0) ## 2.0
+  ##  echo log(9.0, 3.0)  ## 2.0
+  ##  echo log(32.0, 2.0) ## 5.0
+  ##  echo log(0.0, 2.0)  ## -inf
+  ##  echo log(-7.0, 4.0) ## nan
+  ##  echo log(8.0, -2.0) ## nan
   ln(x) / ln(base)
 
 when not defined(JS): # C
@@ -202,77 +325,164 @@ when not defined(JS): # C
   proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
     ## Computes the common logarithm (base 10) of ``x``.
     ##
+    ## See also:
+    ## * `ln proc <#ln,float64>`_
+    ## * `log proc <#log,T,T>`_
+    ## * `log2 proc <#log2,float64>`_
+    ## * `exp proc <#exp,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo log10(100.0) ## 2.0
+    ##  echo log10(100.0)  ## 2.0
+    ##  echo log10(0.0)    ## nan
+    ##  echo log10(-100.0) ## -inf
   proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
   proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
-    ## Computes the exponential function of ``x`` (pow(E, x)).
+    ## Computes the exponential function of ``x`` (e^x).
+    ##
+    ## See also:
+    ## * `ln proc <#ln,float64>`_
+    ## * `log proc <#log,T,T>`_
+    ## * `log10 proc <#log10,float64>`_
+    ## * `log2 proc <#log2,float64>`_
     ##
     ## .. code-block:: nim
-    ##  echo exp(1.0) ## 2.718281828459045
+    ##  echo exp(1.0)     ## 2.718281828459045
     ##  echo ln(exp(4.0)) ## 4.0
+    ##  echo exp(0.0)     ## 1.0
+    ##  echo exp(-1.0)    ## 0.3678794411714423
   proc sin*(x: float32): float32 {.importc: "sinf", header: "<math.h>".}
   proc sin*(x: float64): float64 {.importc: "sin", header: "<math.h>".}
     ## Computes the sine of ``x``.
     ##
+    ## See also:
+    ## * `cos proc <#cos,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `sinh proc <#sinh,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo sin(PI / 6) ## 0.4999999999999999
+    ##  echo sin(PI / 6)         ## 0.4999999999999999
     ##  echo sin(degToRad(90.0)) ## 1.0
   proc cos*(x: float32): float32 {.importc: "cosf", header: "<math.h>".}
   proc cos*(x: float64): float64 {.importc: "cos", header: "<math.h>".}
     ## Computes the cosine of ``x``.
     ##
+    ## See also:
+    ## * `sin proc <#sin,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `cosh proc <#cosh,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo cos(2 * PI) ## 1.0
+    ##  echo cos(2 * PI)         ## 1.0
     ##  echo cos(degToRad(60.0)) ## 0.5000000000000001
   proc tan*(x: float32): float32 {.importc: "tanf", header: "<math.h>".}
   proc tan*(x: float64): float64 {.importc: "tan", header: "<math.h>".}
     ## Computes the tangent of ``x``.
     ##
+    ## See also:
+    ## * `sin proc <#sin,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ##
     ## .. code-block:: nim
     ##  echo tan(degToRad(45.0)) ## 0.9999999999999999
-    ##  echo tan(PI / 4) ## 0.9999999999999999
+    ##  echo tan(PI / 4)         ## 0.9999999999999999
   proc sinh*(x: float32): float32 {.importc: "sinhf", header: "<math.h>".}
   proc sinh*(x: float64): float64 {.importc: "sinh", header: "<math.h>".}
     ## Computes the `hyperbolic sine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `cosh proc <#cosh,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ## * `arcsinh proc <#arcsinh,float64>`_
+    ## * `sin proc <#sin,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo sinh(1.0) ## 1.175201193643801
+    ##  echo sinh(0.0)            ## 0.0
+    ##  echo sinh(1.0)            ## 1.175201193643801
+    ##  echo sinh(degToRad(90.0)) ## 2.301298902307295
   proc cosh*(x: float32): float32 {.importc: "coshf", header: "<math.h>".}
   proc cosh*(x: float64): float64 {.importc: "cosh", header: "<math.h>".}
     ## Computes the `hyperbolic cosine <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `sinh proc <#sinh,float64>`_
+    ## * `tanh proc <#tanh,float64>`_
+    ## * `arccosh proc <#arccosh,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo cosh(1.0) ## 1.543080634815244
+    ##  echo cosh(0.0)            ## 1.0
+    ##  echo cosh(1.0)            ## 1.543080634815244
+    ##  echo cosh(degToRad(90.0)) ## 2.509178478658057
   proc tanh*(x: float32): float32 {.importc: "tanhf", header: "<math.h>".}
   proc tanh*(x: float64): float64 {.importc: "tanh", header: "<math.h>".}
     ## Computes the `hyperbolic tangent <https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions>`_ of ``x``.
     ##
+    ## See also:
+    ## * `sinh proc <#sinh,float64>`_
+    ## * `cosh proc <#cosh,float64>`_
+    ## * `arctanh proc <#arctanh,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo tanh(1.0) ## 0.7615941559557649
+    ##  echo tanh(0.0)            ## 0.0
+    ##  echo tanh(1.0)            ## 0.7615941559557649
+    ##  echo tanh(degToRad(90.0)) ## 0.9171523356672744
 
   proc arccos*(x: float32): float32 {.importc: "acosf", header: "<math.h>".}
   proc arccos*(x: float64): float64 {.importc: "acos", header: "<math.h>".}
     ## Computes the arc cosine of ``x``.
     ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `cos proc <#cos,float64>`_
+    ##
     ## .. code-block:: nim
-    ##  echo arccos(1.0) ## 0.0
+    ##  echo radToDeg(arccos(0.0)) ## 90.0
+    ##  echo radToDeg(arccos(1.0)) ## 0.0
   proc arcsin*(x: float32): float32 {.importc: "asinf", header: "<math.h>".}
   proc arcsin*(x: float64): float64 {.importc: "asin", header: "<math.h>".}
     ## Computes the arc sine of ``x``.
+    ##
+    ## See also:
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `sin proc <#sin,float64>`_
+    ##
+    ## .. code-block:: nim
+    ##  echo radToDeg(arcsin(0.0)) ## 0.0
+    ##  echo radToDeg(arcsin(1.0)) ## 90.0
   proc arctan*(x: float32): float32 {.importc: "atanf", header: "<math.h>".}
   proc arctan*(x: float64): float64 {.importc: "atan", header: "<math.h>".}
     ## Calculate the arc tangent of ``x``.
     ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan2 proc <#arctan2,float64,float64>`_
+    ## * `tan proc <#tan,float64>`_
+    ##
     ## .. code-block:: nim
     ##  echo arctan(1.0) ## 0.7853981633974483
     ##  echo radToDeg(arctan(1.0)) ## 45.0
   proc arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "<math.h>".}
   proc arctan2*(y, x: float64): float64 {.importc: "atan2", header: "<math.h>".}
     ## Calculate the arc tangent of ``y`` / ``x``.
-    ## `arctan2` returns the arc tangent of ``y`` / ``x``; it produces correct
-    ## results even when the resulting angle is near pi/2 or -pi/2
-    ## (``x`` near 0).
+    ##
+    ## It produces correct results even when the resulting angle is near
+    ## pi/2 or -pi/2 (``x`` near 0).
+    ##
+    ## See also:
+    ## * `arcsin proc <#arcsin,float64>`_
+    ## * `arccos proc <#arccos,float64>`_
+    ## * `arctan proc <#arctan,float64>`_
+    ## * `tan proc <#tan,float64>`_
     ##
     ## .. code-block:: nim
     ##  echo arctan2(1.0, 0.0) ## 1.570796326794897
@@ -313,18 +523,18 @@ else: # JS
   proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.}
 
 proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x)
-  ## Computes the cotangent of ``x``.
+  ## Computes the cotangent of ``x`` (1 / tan(x)).
 proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x)
-  ## Computes the secant of ``x``.
+  ## Computes the secant of ``x`` (1 / cos(x)).
 proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x)
-  ## Computes the cosecant of ``x``.
+  ## Computes the cosecant of ``x`` (1 / sin(x)).
 
 proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x)
-  ## Computes the hyperbolic cotangent of ``x``.
+  ## Computes the hyperbolic cotangent of ``x`` (1 / tanh(x)).
 proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x)
-  ## Computes the hyperbolic secant of ``x``.
+  ## Computes the hyperbolic secant of ``x`` (1 / cosh(x)).
 proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x)
-  ## Computes the hyperbolic cosecant of ``x``.
+  ## Computes the hyperbolic cosecant of ``x`` (1 / sinh(x)).
 
 proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x)
   ## Computes the inverse cotangent of ``x``.
@@ -352,11 +562,17 @@ when not defined(JS): # C
     ##  echo hypot(4.0, 3.0) ## 5.0
   proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
   proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
-    ## computes x to power raised of y.
+    ## Computes x to power raised of y.
+    ##
+    ## To compute power between integers (e.g. 2^6), use `^ proc<#^,T,Natural>`_.
     ##
-    ## To compute power between integers, use ``^`` e.g. 2 ^ 6
+    ## See also:
+    ## * `^ proc<#^,T,Natural>`_
+    ## * `sqrt proc <#sqrt,float64>`_
+    ## * `cbrt proc <#cbrt,float64>`_
     ##
     ## .. code-block:: nim
+    ##  echo pow(100, 1.5)  ## 1000.0
     ##  echo pow(16.0, 0.5) ## 4.0
 
   # TODO: add C89 version on windows
@@ -370,6 +586,15 @@ when not defined(JS): # C
     proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
     proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
       ## Computes the the `gamma function <https://en.wikipedia.org/wiki/Gamma_function>`_ for ``x``.
+      ##
+      ## See also:
+      ## * `lgamma proc <#lgamma,float64>`_ for a natural log of gamma function
+      ##
+      ## .. code-block:: Nim
+      ##  echo gamma(1.0)  # 1.0
+      ##  echo gamma(4.0)  # 6.0
+      ##  echo gamma(11.0) # 3628800.0
+      ##  echo gamma(-1.0) # nan
     proc tgamma*(x: float32): float32
       {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
     proc tgamma*(x: float64): float64
@@ -379,19 +604,43 @@ when not defined(JS): # C
     proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
     proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
       ## Computes the natural log of the gamma function for ``x``.
+      ##
+      ## See also:
+      ## * `gamma proc <#gamma,float64>`_ for gamma function
+      ##
+      ## .. code-block:: Nim
+      ##  echo lgamma(1.0)  # 1.0
+      ##  echo lgamma(4.0)  # 1.791759469228055
+      ##  echo lgamma(11.0) # 15.10441257307552
+      ##  echo lgamma(-1.0) # inf
 
   proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
   proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
     ## Computes the floor function (i.e., the largest integer not greater than ``x``).
     ##
+    ## See also:
+    ## * `ceil proc <#ceil,float64>`_
+    ## * `round proc <#round,float64>`_
+    ## * `trunc proc <#trunc,float64>`_
+    ##
     ## .. code-block:: nim
+    ##  echo floor(2.1)  ## 2.0
+    ##  echo floor(2.9)  ## 2.0
     ##  echo floor(-3.5) ## -4.0
 
   proc ceil*(x: float32): float32 {.importc: "ceilf", header: "<math.h>".}
   proc ceil*(x: float64): float64 {.importc: "ceil", header: "<math.h>".}
-    ## Computes the ceiling function (i.e., the smallest integer not less than ``x``).
+    ## Computes the ceiling function (i.e., the smallest integer not smaller
+    ## than ``x``).
+    ##
+    ## See also:
+    ## * `floor proc <#floor,float64>`_
+    ## * `round proc <#round,float64>`_
+    ## * `trunc proc <#trunc,float64>`_
     ##
     ## .. code-block:: nim
+    ##  echo ceil(2.1)  ## 3.0
+    ##  echo ceil(2.9)  ## 3.0
     ##  echo ceil(-2.1) ## -2.0
 
   when windowsCC89:
@@ -452,26 +701,50 @@ when not defined(JS): # C
   else:
     proc round*(x: float32): float32 {.importc: "roundf", header: "<math.h>".}
     proc round*(x: float64): float64 {.importc: "round", header: "<math.h>".}
-      ## Rounds a float to zero decimal places.  Used internally by the round
-      ## function when the specified number of places is 0.
+      ## Rounds a float to zero decimal places.
+      ##
+      ## Used internally by the `round proc <#round,T,int>`_
+      ## when the specified number of places is 0.
+      ##
+      ## See also:
+      ## * `round proc <#round,T,int>`_ for rounding to the specific
+      ##   number of decimal places
+      ## * `floor proc <#floor,float64>`_
+      ## * `ceil proc <#ceil,float64>`_
+      ## * `trunc proc <#trunc,float64>`_
+      ##
+      ## .. code-block:: nim
+      ##   echo round(3.4) ## 3.0
+      ##   echo round(3.5) ## 4.0
+      ##   echo round(4.5) ## 5.0
 
     proc trunc*(x: float32): float32 {.importc: "truncf", header: "<math.h>".}
     proc trunc*(x: float64): float64 {.importc: "trunc", header: "<math.h>".}
       ## Truncates ``x`` to the decimal point.
       ##
+      ## See also:
+      ## * `floor proc <#floor,float64>`_
+      ## * `ceil proc <#ceil,float64>`_
+      ## * `round proc <#round,float64>`_
+      ##
       ## .. code-block:: nim
       ##  echo trunc(PI) # 3.0
       ##  echo trunc(-1.85) # -1.0
 
   proc fmod*(x, y: float32): float32 {.deprecated: "use mod instead", importc: "fmodf", header: "<math.h>".}
   proc fmod*(x, y: float64): float64 {.deprecated: "use mod instead", importc: "fmod", header: "<math.h>".}
+    ## **Deprecated since version 0.19.0**: Use the `mod proc
+    ## <#mod,float64,float64>`_ instead.
+    ##
     ## Computes the remainder of ``x`` divided by ``y``.
-    ## **Deprecated since version 0.19.0**: Use the ``mod`` operator instead.
 
   proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "<math.h>".}
   proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "<math.h>".}
     ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``).
     ##
+    ## See also:
+    ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior
+    ##
     ## .. code-block:: nim
     ##  ( 6.5 mod  2.5) ==  1.5
     ##  (-6.5 mod  2.5) == -1.5
@@ -502,16 +775,22 @@ else: # JS
     ##  (-6.5 mod -2.5) == -1.5
 
 proc round*[T: float32|float64](x: T, places: int): T {.deprecated: "use format instead".} =
+  ## **Deprecated:** use `strformat module <strformat.html>`_
+  ##
   ## Decimal rounding on a binary floating point number.
   ##
   ## This function is NOT reliable. Floating point numbers cannot hold
-  ## non integer decimals precisely.  If ``places`` is 0 (or omitted),
+  ## non integer decimals precisely. If ``places`` is 0 (or omitted),
   ## round to the nearest integral value following normal mathematical
-  ## rounding rules (e.g.  ``round(54.5) -> 55.0``).  If ``places`` is
+  ## rounding rules (e.g.  ``round(54.5) -> 55.0``). If ``places`` is
   ## greater than 0, round to the given number of decimal places,
-  ## e.g. ``round(54.346, 2) -> 54.350000000000001421...``.  If ``places`` is negative, round
-  ## to the left of the decimal place, e.g.  ``round(537.345, -1) ->
+  ## e.g. ``round(54.346, 2) -> 54.350000000000001421…``. If ``places`` is negative, round
+  ## to the left of the decimal place, e.g. ``round(537.345, -1) ->
   ## 540.0``
+  ##
+  ## .. code-block:: Nim
+  ##  echo round(PI, 2) ## 3.14
+  ##  echo round(PI, 4) ## 3.1416
   if places == 0:
     result = round(x)
   else:
@@ -520,9 +799,14 @@ proc round*[T: float32|float64](x: T, places: int): T {.deprecated: "use format
 
 proc floorDiv*[T: SomeInteger](x, y: T): T =
   ## Floor division is conceptually defined as ``floor(x / y)``.
-  ## This is different from the ``div`` operator, which is defined
-  ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv``
-  ## rounds down.
+  ##
+  ## This is different from the `system.div <system.html#div,int,int>`_
+  ## operator, which is defined as ``trunc(x / y)``.
+  ## That is, ``div`` rounds towards ``0`` and ``floorDiv`` rounds down.
+  ##
+  ## See also:
+  ## * `system.div proc <system.html#div,int,int>`_ for integer division
+  ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior
   ##
   ## .. code-block:: nim
   ##  echo floorDiv( 13,  3) #  4
@@ -535,8 +819,13 @@ proc floorDiv*[T: SomeInteger](x, y: T): T =
 
 proc floorMod*[T: SomeNumber](x, y: T): T =
   ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y)``.
+  ##
   ## This proc behaves the same as the ``%`` operator in Python.
   ##
+  ## See also:
+  ## * `mod proc <#mod,float64,float64>`_
+  ## * `floorDiv proc <#floorDiv,T,T>`_
+  ##
   ## .. code-block:: nim
   ##  echo floorMod( 13,  3) #  1
   ##  echo floorMod(-13,  3) #  2
@@ -552,6 +841,7 @@ when not defined(JS):
     importc: "frexp", header: "<math.h>".}
   proc frexp*[T, U](x: T, exponent: var U): T =
     ## Split a number into mantissa and exponent.
+    ##
     ## ``frexp`` calculates the mantissa m (a float greater than or equal to 0.5
     ## and less than 1) and the integer value n such that ``x`` (the original
     ## float value) equals ``m * 2**n``. frexp stores n in `exponent` and returns
@@ -584,7 +874,19 @@ when not defined(JS):
   else:
     proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
     proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
-      ## Computes the binary logarithm (base 2) of ``x``
+      ## Computes the binary logarithm (base 2) of ``x``.
+      ##
+      ## See also:
+      ## * `log proc <#log,T,T>`_
+      ## * `log10 proc <#log10,float64>`_
+      ## * `ln proc <#ln,float64>`_
+      ## * `exp proc <#exp,float64>`_
+      ##
+      ## .. code-block:: Nim
+      ##  echo log2(8.0)  # 3.0
+      ##  echo log2(1.0)  # 0.0
+      ##  echo log2(0.0)  # -inf
+      ##  echo log2(-2.0) # nan
 
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
@@ -613,7 +915,8 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## function in C.
   ##
   ## .. code-block:: nim
-  ##  echo splitDecimal(5.25) # (intpart: 5.0, floatpart: 0.25)
+  ##  echo splitDecimal(5.25)  # (intpart: 5.0, floatpart: 0.25)
+  ##  echo splitDecimal(-2.73) # (intpart: -2.0, floatpart: -0.73)
   var
     absolute: T
   absolute = abs(x)
@@ -626,26 +929,36 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
 {.pop.}
 
 proc degToRad*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from degrees to radians
+  ## Convert from degrees to radians.
+  ##
+  ## See also:
+  ## * `radToDeg proc <#radToDeg,T>`_
   ##
   ## .. code-block:: nim
   ##  echo degToRad(180.0) # 3.141592653589793
   result = T(d) * RadPerDeg
 
 proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
-  ## Convert from radians to degrees
-
+  ## Convert from radians to degrees.
+  ##
+  ## See also:
+  ## * `degToRad proc <#degToRad,T>`_
+  ##
   ## .. code-block:: nim
   ##  echo degToRad(2 * PI) # 360.0
   result = T(d) / RadPerDeg
 
 proc sgn*[T: SomeNumber](x: T): int {.inline.} =
-  ## Sign function. Returns -1 for negative numbers and ``NegInf``, 1 for
-  ## positive numbers and ``Inf``, and 0 for positive zero, negative zero and
-  ## ``NaN``.
+  ## Sign function.
+  ##
+  ## Returns:
+  ## * `-1` for negative numbers and ``NegInf``,
+  ## * `1` for positive numbers and ``Inf``,
+  ## * `0` for positive zero, negative zero and ``NaN``
   ##
   ## .. code-block:: nim
-  ##  echo sgn(-5) # 1
+  ##  echo sgn(5)    # 1
+  ##  echo sgn(0)    # 0
   ##  echo sgn(-4.1) # -1
   ord(T(0) < x) - ord(x < T(0))
 
@@ -653,11 +966,20 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
 {.pop.}
 
 proc `^`*[T](x: T, y: Natural): T =
-  ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
-  ## `pow <#pow,float,float>`_ for negative exponents.
+  ## Computes ``x`` to the power ``y``.
+  ##
+  ## Exponent ``y`` must be non-negative, use
+  ## `pow proc <#pow,float64,float64>`_ for negative exponents.
+  ##
+  ## See also:
+  ## * `pow proc <#pow,float64,float64>`_ for negative exponent or
+  ##   floats
+  ## * `sqrt proc <#sqrt,float64>`_
+  ## * `cbrt proc <#cbrt,float64>`_
   ##
   ## .. code-block:: nim
-  ##  echo 2 ^ 3 # 8
+  ##  echo 2^3  # 8
+  ##  echo -2^3 # -8
   when compiles(y >= T(0)):
     assert y >= T(0)
   else:
@@ -675,9 +997,16 @@ proc `^`*[T](x: T, y: Natural): T =
 
 proc gcd*[T](x, y: T): T =
   ## Computes the greatest common (positive) divisor of ``x`` and ``y``.
+  ##
   ## Note that for floats, the result cannot always be interpreted as
   ## "greatest decimal `z` such that ``z*N == x and z*M == y``
   ## where N and M are positive integers."
+  ##
+  ## See also:
+  ## * `gcd proc <#gcd,SomeInteger,SomeInteger>`_ for integer version
+  ## * `lcm proc <#lcm,T,T>`_
+  runnableExamples:
+    doAssert gcd(13.5, 9.0) == 4.5
   var (x, y) = (x, y)
   while y != 0:
     x = x mod y
@@ -685,11 +1014,15 @@ proc gcd*[T](x, y: T): T =
   abs x
 
 proc gcd*(x, y: SomeInteger): SomeInteger =
-  ## Computes the greatest common (positive) divisor of ``x`` and ``y``.
-  ## Using binary GCD (aka Stein's) algorithm.
+  ## Computes the greatest common (positive) divisor of ``x`` and ``y``,
+  ## using binary GCD (aka Stein's) algorithm.
   ##
-  ## .. code-block:: nim
-  ##  echo gcd(24, 30) # 6
+  ## See also:
+  ## * `gcd proc <#gcd,T,T>`_ for floats version
+  ## * `lcm proc <#lcm,T,T>`_
+  runnableExamples:
+    doAssert gcd(12, 8) == 4
+    doAssert gcd(17, 63) == 1
   when x is SomeSignedInt:
     var x = abs(x)
   else:
@@ -716,10 +1049,15 @@ proc gcd*(x, y: SomeInteger): SomeInteger =
 proc lcm*[T](x, y: T): T =
   ## Computes the least common multiple of ``x`` and ``y``.
   ##
-  ## .. code-block:: nim
-  ##  echo lcm(24, 30) # 120
+  ## See also:
+  ## * `gcd proc <#gcd,T,T>`_
+  runnableExamples:
+    doAssert lcm(24, 30) == 120
+    doAssert lcm(13, 39) == 39
   x div gcd(x, y) * y
 
+
+
 when isMainModule and not defined(JS) and not windowsCC89:
   # Check for no side effect annotation
   proc mySqrt(num: float): float {.noSideEffect.} =
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index 810223d72..d1a006eee 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -42,9 +42,10 @@ type
       wasOpened*: bool     ## **Caution**: Windows specific public field.
     else:
       handle*: cint        ## **Caution**: Posix specific public field.
+      flags: cint          ## **Caution**: Platform specific private field.
 
 proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
-             mappedSize = -1, offset = 0): pointer =
+             mappedSize = -1, offset = 0, mapFlags = cint(-1)): pointer =
   ## returns a pointer to a mapped portion of MemFile `m`
   ##
   ## ``mappedSize`` of ``-1`` maps to the whole file, and
@@ -65,11 +66,17 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
       raiseOSError(osLastError())
   else:
     assert mappedSize > 0
+
+    m.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags
+    #Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
+    if int(m.flags and MAP_PRIVATE) == 0:
+      m.flags = m.flags or MAP_SHARED
+
     result = mmap(
       nil,
       mappedSize,
       if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
-      if readonly: (MAP_PRIVATE or MAP_POPULATE) else: (MAP_SHARED or MAP_POPULATE),
+      m.flags,
       m.handle, offset)
     if result == cast[pointer](MAP_FAILED):
       raiseOSError(osLastError())
@@ -90,7 +97,7 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) =
 
 proc open*(filename: string, mode: FileMode = fmRead,
            mappedSize = -1, offset = 0, newFileSize = -1,
-           allowRemap = false): MemFile =
+           allowRemap = false, mapFlags = cint(-1)): MemFile =
   ## opens a memory mapped file. If this fails, ``OSError`` is raised.
   ##
   ## ``newFileSize`` can only be set if the file does not exist and is opened
@@ -105,6 +112,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
   ## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on
   ## the resulting MemFile; else file handles are not kept open.
   ##
+  ## ``mapFlags`` allows callers to override default choices for memory mapping
+  ## flags with a bitwise mask of a variety of likely platform-specific flags
+  ## which may be ignored or even cause `open` to fail if misspecified.
+  ##
   ## Example:
   ##
   ## .. code-block:: nim
@@ -245,11 +256,16 @@ proc open*(filename: string, mode: FileMode = fmRead,
       else:
         fail(osLastError(), "error getting file size")
 
+    result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags
+    #Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
+    if int(result.flags and MAP_PRIVATE) == 0:
+      result.flags = result.flags or MAP_SHARED
+
     result.mem = mmap(
       nil,
       result.size,
       if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
-      if readonly: (MAP_PRIVATE or MAP_POPULATE) else: (MAP_SHARED or MAP_POPULATE),
+      result.flags,
       result.handle,
       offset)
 
@@ -305,7 +321,7 @@ when defined(posix) or defined(nimdoc):
         if munmap(f.mem, f.size) != 0:
           raiseOSError(osLastError())
         let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
-                            MAP_SHARED or MAP_POPULATE, f.handle, 0)
+                           f.flags, f.handle, 0)
         if newAddr == cast[pointer](MAP_FAILED):
           raiseOSError(osLastError())
       f.mem = newAddr
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index f98f9a444..6d4df1c5d 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -71,6 +71,7 @@ type
     IPPROTO_IPV6,       ## Internet Protocol Version 6. Unsupported on Windows.
     IPPROTO_RAW,        ## Raw IP Packets Protocol. Unsupported on Windows.
     IPPROTO_ICMP        ## Control message protocol. Unsupported on Windows.
+    IPPROTO_ICMPV6      ## Control message protocol for IPv6. Unsupported on Windows.
 
   Servent* = object ## information about a service
     name*: string
@@ -154,6 +155,7 @@ when not useWinVersion:
     of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
     of IPPROTO_RAW:    result = posix.IPPROTO_RAW
     of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
+    of IPPROTO_ICMPV6:   result = posix.IPPROTO_ICMPV6
 
 else:
   proc toInt(domain: Domain): cint =
@@ -179,7 +181,7 @@ proc toSockType*(protocol: Protocol): SockType =
     SOCK_STREAM
   of IPPROTO_UDP:
     SOCK_DGRAM
-  of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP:
+  of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6:
     SOCK_RAW
 
 proc createNativeSocket*(domain: Domain = AF_INET,
@@ -255,17 +257,14 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku):
     if domain == AF_INET6:
       hints.ai_flags = AI_V4MAPPED
-  var gaiResult = getaddrinfo(address, $port, addr(hints), result)
+  let socket_port = if sockType == SOCK_RAW: "" else: $port
+  var gaiResult = getaddrinfo(address, socket_port, addr(hints), result)
   if gaiResult != 0'i32:
     when useWinVersion:
       raiseOSError(osLastError())
     else:
       raiseOSError(osLastError(), $gai_strerror(gaiResult))
 
-proc dealloc*(ai: ptr AddrInfo) {.deprecated.} =
-  ## Deprecated since 0.16.2. Use ``freeAddrInfo`` instead.
-  freeaddrinfo(ai)
-
 proc ntohl*(x: uint32): uint32 =
   ## Converts 32-bit unsigned integers from network to host byte order.
   ## On machines where the host byte order is the same as network byte order,
@@ -276,15 +275,6 @@ proc ntohl*(x: uint32): uint32 =
                  (x shl 8'u32 and 0xff0000'u32) or
                  (x shl 24'u32)
 
-template ntohl*(x: int32): untyped {.deprecated.} =
-  ## Converts 32-bit integers from network to host byte order.
-  ## On machines where the host byte order is the same as network byte order,
-  ## this is a no-op; otherwise, it performs a 4-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, IPv4
-  ## addresses are now treated as unsigned integers. Please use the unsigned
-  ## version of this template.
-  cast[int32](nativesockets.ntohl(cast[uint32](x)))
-
 proc ntohs*(x: uint16): uint16 =
   ## Converts 16-bit unsigned integers from network to host byte order. On
   ## machines where the host byte order is the same as network byte order,
@@ -292,39 +282,12 @@ proc ntohs*(x: uint16): uint16 =
   when cpuEndian == bigEndian: result = x
   else: result = (x shr 8'u16) or (x shl 8'u16)
 
-template ntohs*(x: int16): untyped {.deprecated.} =
-  ## Converts 16-bit integers from network to host byte order. On
-  ## machines where the host byte order is the same as network byte order,
-  ## this is a no-op; otherwise, it performs a 2-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, where port
-  ## numbers became unsigned integers. Please use the unsigned version of
-  ## this template.
-  cast[int16](nativesockets.ntohs(cast[uint16](x)))
-
-template htonl*(x: int32): untyped {.deprecated.} =
-  ## Converts 32-bit integers from host to network byte order. On machines
-  ## where the host byte order is the same as network byte order, this is
-  ## a no-op; otherwise, it performs a 4-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, IPv4
-  ## addresses are now treated as unsigned integers. Please use the unsigned
-  ## version of this template.
-  nativesockets.ntohl(x)
-
 template htonl*(x: uint32): untyped =
   ## Converts 32-bit unsigned integers from host to network byte order. On
   ## machines where the host byte order is the same as network byte order,
   ## this is a no-op; otherwise, it performs a 4-byte swap operation.
   nativesockets.ntohl(x)
 
-template htons*(x: int16): untyped {.deprecated.} =
-  ## Converts 16-bit integers from host to network byte order.
-  ## On machines where the host byte order is the same as network byte
-  ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
-  ## **Warning**: This template is deprecated since 0.14.0, where port
-  ## numbers became unsigned integers. Please use the unsigned version of
-  ## this template.
-  nativesockets.ntohs(x)
-
 template htons*(x: uint16): untyped =
   ## Converts 16-bit unsigned integers from host to network byte order.
   ## On machines where the host byte order is the same as network byte
@@ -646,29 +609,6 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
       inc(i)
   setLen(s, L)
 
-proc select*(readfds: var seq[SocketHandle], timeout = 500): int {.deprecated: "use selectRead instead".} =
-  ## When a socket in ``readfds`` is ready to be read from then a non-zero
-  ## value will be returned specifying the count of the sockets which can be
-  ## read from. The sockets which can be read from will also be removed
-  ## from ``readfds``.
-  ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
-  ## an unlimited time.
-  ## **Warning:** This is deprecated since version 0.16.2.
-  ## Use the ``selectRead`` procedure instead.
-  var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
-
-  var rd: TFdSet
-  var m = 0
-  createFdSet((rd), readfds, m)
-
-  if timeout != -1:
-    result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
-  else:
-    result = int(select(cint(m+1), addr(rd), nil, nil, nil))
-
-  pruneSocketSet(readfds, (rd))
-
 proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
   ## When a socket in ``readfds`` is ready to be read from then a non-zero
   ## value will be returned specifying the count of the sockets which can be
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 840a81f17..a5bdf3ce6 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -233,7 +233,7 @@ proc parseIPv4Address(addressStr: string): IpAddress =
   var
     byteCount = 0
     currentByte:uint16 = 0
-    seperatorValid = false
+    separatorValid = false
 
   result.family = IpAddressFamily.IPv4
 
@@ -244,20 +244,20 @@ proc parseIPv4Address(addressStr: string): IpAddress =
       if currentByte > 255'u16:
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
-      seperatorValid = true
+      separatorValid = true
     elif addressStr[i] == '.': # IPv4 address separator
-      if not seperatorValid or byteCount >= 3:
+      if not separatorValid or byteCount >= 3:
         raise newException(ValueError,
           "Invalid IP Address. The address consists of too many groups")
       result.address_v4[byteCount] = cast[uint8](currentByte)
       currentByte = 0
       byteCount.inc
-      seperatorValid = false
+      separatorValid = false
     else:
       raise newException(ValueError,
         "Invalid IP Address. Address contains an invalid character")
 
-  if byteCount != 3 or not seperatorValid:
+  if byteCount != 3 or not separatorValid:
     raise newException(ValueError, "Invalid IP Address")
   result.address_v4[byteCount] = cast[uint8](currentByte)
 
@@ -272,7 +272,7 @@ proc parseIPv6Address(addressStr: string): IpAddress =
     groupCount = 0
     currentGroupStart = 0
     currentShort:uint32 = 0
-    seperatorValid = true
+    separatorValid = true
     dualColonGroup = -1
     lastWasColon = false
     v4StartPos = -1
@@ -280,15 +280,15 @@ proc parseIPv6Address(addressStr: string): IpAddress =
 
   for i,c in addressStr:
     if c == ':':
-      if not seperatorValid:
+      if not separatorValid:
         raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid seperator")
+          "Invalid IP Address. Address contains an invalid separator")
       if lastWasColon:
         if dualColonGroup != -1:
           raise newException(ValueError,
-            "Invalid IP Address. Address contains more than one \"::\" seperator")
+            "Invalid IP Address. Address contains more than one \"::\" separator")
         dualColonGroup = groupCount
-        seperatorValid = false
+        separatorValid = false
       elif i != 0 and i != high(addressStr):
         if groupCount >= 8:
           raise newException(ValueError,
@@ -297,7 +297,7 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
         currentShort = 0
         groupCount.inc()
-        if dualColonGroup != -1: seperatorValid = false
+        if dualColonGroup != -1: separatorValid = false
       elif i == 0: # only valid if address starts with ::
         if addressStr[1] != ':':
           raise newException(ValueError,
@@ -309,11 +309,11 @@ proc parseIPv6Address(addressStr: string): IpAddress =
       lastWasColon = true
       currentGroupStart = i + 1
     elif c == '.': # Switch to parse IPv4 mode
-      if i < 3 or not seperatorValid or groupCount >= 7:
+      if i < 3 or not separatorValid or groupCount >= 7:
         raise newException(ValueError, "Invalid IP Address")
       v4StartPos = currentGroupStart
       currentShort = 0
-      seperatorValid = false
+      separatorValid = false
       break
     elif c in strutils.HexDigits:
       if c in strutils.Digits: # Normal digit
@@ -326,14 +326,14 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
       lastWasColon = false
-      seperatorValid = true
+      separatorValid = true
     else:
       raise newException(ValueError,
         "Invalid IP Address. Address contains an invalid character")
 
 
   if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
-    if seperatorValid: # Copy remaining data
+    if separatorValid: # Copy remaining data
       if groupCount >= 8:
         raise newException(ValueError,
           "Invalid IP Address. The address consists of too many groups")
@@ -347,19 +347,19 @@ proc parseIPv6Address(addressStr: string): IpAddress =
         if currentShort > 255'u32:
           raise newException(ValueError,
             "Invalid IP Address. Value is out of range")
-        seperatorValid = true
+        separatorValid = true
       elif c == '.': # IPv4 address separator
-        if not seperatorValid or byteCount >= 3:
+        if not separatorValid or byteCount >= 3:
           raise newException(ValueError, "Invalid IP Address")
         result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
         currentShort = 0
         byteCount.inc()
-        seperatorValid = false
+        separatorValid = false
       else: # Invalid character
         raise newException(ValueError,
           "Invalid IP Address. Address contains an invalid character")
 
-    if byteCount != 3 or not seperatorValid:
+    if byteCount != 3 or not separatorValid:
       raise newException(ValueError, "Invalid IP Address")
     result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
     groupCount += 2
@@ -967,39 +967,6 @@ when defined(posix) or defined(nimdoc):
         raiseOSError(osLastError())
 
 when defined(ssl):
-  proc handshake*(socket: Socket): bool
-    {.tags: [ReadIOEffect, WriteIOEffect], deprecated.} =
-    ## This proc needs to be called on a socket after it connects. This is
-    ## only applicable when using ``connectAsync``.
-    ## This proc performs the SSL handshake.
-    ##
-    ## Returns ``False`` whenever the socket is not yet ready for a handshake,
-    ## ``True`` whenever handshake completed successfully.
-    ##
-    ## A SslError error is raised on any other errors.
-    ##
-    ## **Note:** This procedure is deprecated since version 0.14.0.
-    result = true
-    if socket.isSSL:
-      var ret = SSLConnect(socket.sslHandle)
-      if ret <= 0:
-        var errret = SSLGetError(socket.sslHandle, ret)
-        case errret
-        of SSL_ERROR_ZERO_RETURN:
-          raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
-        of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
-          SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE:
-          return false
-        of SSL_ERROR_WANT_X509_LOOKUP:
-          raiseSSLError("Function for x509 lookup has been called.")
-        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
-          raiseSSLError()
-        else:
-          raiseSSLError("Unknown Error")
-      socket.sslNoHandshake = false
-    else:
-      raiseSSLError("Socket is not an SSL socket.")
-
   proc gotHandshake*(socket: Socket): bool =
     ## Determines whether a handshake has occurred between a client (``socket``)
     ## and the server that ``socket`` is connected to.
@@ -1026,7 +993,7 @@ proc select(readfd: Socket, timeout = 500): int =
     return 1
 
   var fds = @[readfd.fd]
-  result = select(fds, timeout)
+  result = selectRead(fds, timeout)
 
 proc isClosed(socket: Socket): bool =
   socket.fd == osInvalidSocket
@@ -1694,7 +1661,5 @@ proc connect*(socket: Socket, address: string, port = Port(0),
     when defineSsl and not defined(nimdoc):
       if socket.isSSL:
         socket.fd.setBlocking(true)
-        {.warning[Deprecated]: off.}
-        doAssert socket.handshake()
-        {.warning[Deprecated]: on.}
+        doAssert socket.gotHandshake()
   socket.fd.setBlocking(true)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 6521d827c..181bc5728 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -19,7 +19,9 @@ include "system/inclrtl"
 import
   strutils, pathnorm
 
-when defined(nimscript):
+const weirdTarget = defined(nimscript) or defined(js)
+
+when weirdTarget:
   discard
 elif defined(windows):
   import winlean, times
@@ -31,7 +33,7 @@ elif defined(posix):
 else:
   {.error: "OS module not ported to your operating system!".}
 
-when defined(nimscript) and defined(nimErrorProcCanHaveBody):
+when weirdTarget and defined(nimErrorProcCanHaveBody):
   {.pragma: noNimScript, error: "this proc is not available on the NimScript target".}
 else:
   {.pragma: noNimScript.}
@@ -334,7 +336,7 @@ proc searchExtPos*(path: string): int =
 
 proc splitFile*(path: string): tuple[dir, name, ext: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a filename into (dir, filename, extension).
+  ## Splits a filename into (dir, name, extension).
   ## `dir` does not end in `DirSep`.
   ## `extension` includes the leading dot.
   ##
@@ -349,19 +351,30 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
   ## If `path` has no extension, `ext` is the empty string.
   ## If `path` has no directory component, `dir` is the empty string.
   ## If `path` has no filename component, `name` and `ext` are empty strings.
-  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
-    result = (path, "", "")
+  if path.len == 0:
+      result = ("", "", "")
+  elif path[^1] in {DirSep, AltSep}:
+    if path.len == 1:
+      # issue #8255
+      result = ($path[0], "", "")
+    else:
+      result = (path[0 ..< ^1], "", "")
   else:
     var sepPos = -1
     var dotPos = path.len
     for i in countdown(len(path)-1, 0):
       if path[i] == ExtSep:
         if dotPos == path.len and i > 0 and
-            path[i-1] notin {DirSep, AltSep}: dotPos = i
+            path[i-1] notin {DirSep, AltSep, ExtSep}: dotPos = i
       elif path[i] in {DirSep, AltSep}:
         sepPos = i
         break
-    result.dir = substr(path, 0, sepPos-1)
+    if sepPos-1 >= 0:
+      result.dir = substr(path, 0, sepPos-1)
+    elif path[0] in {DirSep, AltSep}:
+      # issue #8255
+      result.dir = $path[0]
+
     result.name = substr(path, sepPos+1, dotPos-1)
     result.ext = substr(path, dotPos)
 
@@ -651,7 +664,7 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
       if i > 0: result.add " "
       result.add quoteShell(args[i])
 
-when not defined(nimscript):
+when not weirdTarget:
   proc c_rename(oldname, newname: cstring): cint {.
     importc: "rename", header: "<stdio.h>".}
   proc c_system(cmd: cstring): cint {.
@@ -662,7 +675,7 @@ when not defined(nimscript):
     importc: "free", header: "<stdlib.h>".}
 
 
-when defined(windows) and not defined(nimscript):
+when defined(windows) and not weirdTarget:
   when useWinUnicode:
     template wrapUnary(varname, winApiProc, arg: untyped) =
       var varname = winApiProc(newWideCString(arg))
@@ -744,7 +757,7 @@ proc dirExists*(dir: string): bool {.inline, noNimScript.} =
   ## Synonym for existsDir
   existsDir(dir)
 
-when not defined(windows) and not defined(nimscript):
+when not defined(windows) and not weirdTarget:
   proc checkSymlink(path: string): bool =
     var rawInfo: Stat
     if lstat(path, rawInfo) < 0'i32: result = false
@@ -805,7 +818,7 @@ proc findExe*(exe: string, followSymlinks: bool = true;
         return x
   result = ""
 
-when defined(nimscript):
+when weirdTarget:
   const times = "fake const"
   template Time(x: untyped): untyped = string
 
@@ -920,7 +933,7 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} =
   else:
     if chdir(newDir) != 0'i32: raiseOSError(osLastError())
 
-when not defined(nimscript):
+when not weirdTarget:
   proc absolutePath*(path: string, root = getCurrentDir()): string {.noNimScript.} =
     ## Returns the absolute path of `path`, rooted at `root` (which must be absolute)
     ## if `path` is absolute, return it, ignoring `root`
@@ -932,48 +945,6 @@ when not defined(nimscript):
         raise newException(ValueError, "The specified root is not absolute: " & root)
       joinPath(root, path)
 
-proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect], noNimScript.} =
-  ## Returns the full (`absolute`:idx:) path of an existing file `filename`,
-  ## raises OSError in case of an error. Follows symlinks.
-  when defined(windows):
-    var bufsize = MAX_PATH.int32
-    when useWinUnicode:
-      var unused: WideCString = nil
-      var res = newWideCString("", bufsize)
-      while true:
-        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          res = newWideCString("", L)
-          bufsize = L
-        else:
-          result = res$L
-          break
-    else:
-      var unused: cstring = nil
-      result = newString(bufsize)
-      while true:
-        var L = getFullPathNameA(filename, bufsize, result, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError())
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
-  else:
-    # according to Posix we don't need to allocate space for result pathname.
-    # But we need to free return value with free(3).
-    var r = realpath(filename, nil)
-    if r.isNil:
-      raiseOSError(osLastError())
-    else:
-      result = $r
-      c_free(cast[pointer](r))
-
 proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScript.} =
   ## Normalize a path.
   ##
@@ -984,38 +955,39 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScr
   ##
   ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected.
   ## Triple dot is not handled.
-  let isAbs = isAbsolute(path)
-  var stack: seq[string] = @[]
-  for p in split(path, {DirSep}):
-    case p
-    of "", ".":
-      continue
-    of "..":
-      if stack.len == 0:
-        if isAbs:
-          discard  # collapse all double dots on absoluta paths
-        else:
+  path = pathnorm.normalizePath(path)
+  when false:
+    let isAbs = isAbsolute(path)
+    var stack: seq[string] = @[]
+    for p in split(path, {DirSep}):
+      case p
+      of "", ".":
+        continue
+      of "..":
+        if stack.len == 0:
+          if isAbs:
+            discard  # collapse all double dots on absoluta paths
+          else:
+            stack.add(p)
+        elif stack[^1] == "..":
           stack.add(p)
-      elif stack[^1] == "..":
-        stack.add(p)
+        else:
+          discard stack.pop()
       else:
-        discard stack.pop()
-    else:
-      stack.add(p)
+        stack.add(p)
 
-  if isAbs:
-    path = DirSep & join(stack, $DirSep)
-  elif stack.len > 0:
-    path = join(stack, $DirSep)
-  else:
-    path = "."
+    if isAbs:
+      path = DirSep & join(stack, $DirSep)
+    elif stack.len > 0:
+      path = join(stack, $DirSep)
+    else:
+      path = "."
 
 proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
   ## Returns a normalized path for the current OS. See `<#normalizePath>`_
-  result = path
-  normalizePath(result)
+  result = pathnorm.normalizePath(path)
 
-when defined(Windows) and not defined(nimscript):
+when defined(Windows) and not weirdTarget:
   proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =
     var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
     if not followSymlink:
@@ -1238,7 +1210,7 @@ when not declared(ENOENT) and not defined(Windows):
   else:
     var ENOENT {.importc, header: "<errno.h>".}: cint
 
-when defined(Windows) and not defined(nimscript):
+when defined(Windows) and not weirdTarget:
   when useWinUnicode:
     template deleteFile(file: untyped): untyped  = deleteFileW(file)
     template setFileAttributes(file, attrs: untyped): untyped =
@@ -1315,6 +1287,17 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
         discard tryRemoveFile(dest)
         raise
 
+proc exitStatusLikeShell*(status: cint): cint =
+  ## converts exit code from `c_system` into a shell exit code
+  when defined(posix) and not weirdTarget:
+    if WIFSIGNALED(status):
+      # like the shell!
+      128 + WTERMSIG(status)
+    else:
+      WEXITSTATUS(status)
+  else:
+    status
+
 proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   tags: [ExecIOEffect], noNimScript.} =
   ## Executes a `shell command`:idx:.
@@ -1323,23 +1306,19 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   ## line arguments given to program. The proc returns the error code
   ## of the shell when it has finished. The proc does not return until
   ## the process has finished. To execute a program without having a
-  ## shell involved, use the `execProcess` proc of the `osproc`
-  ## module.
-  when defined(posix):
-    result = c_system(command) shr 8
-  else:
-    result = c_system(command)
+  ## shell involved, use `osproc.execProcess`.
+  result = exitStatusLikeShell(c_system(command))
 
 # Templates for filtering directories and files
-when defined(windows) and not defined(nimscript):
+when defined(windows) and not weirdTarget:
   template isDir(f: WIN32_FIND_DATA): bool =
     (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
   template isFile(f: WIN32_FIND_DATA): bool =
     not isDir(f)
 else:
-  template isDir(f: string): bool =
+  template isDir(f: string): bool {.dirty.} =
     dirExists(f)
-  template isFile(f: string): bool =
+  template isFile(f: string): bool {.dirty.} =
     fileExists(f)
 
 template defaultWalkFilter(item): bool =
@@ -1411,6 +1390,54 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript
   ## notation is supported.
   walkCommon(pattern, isDir)
 
+proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noNimScript.} =
+  ## Returns the full (`absolute`:idx:) path of an existing file `filename`,
+  ## raises OSError in case of an error. Follows symlinks.
+  when defined(windows):
+    var bufsize = MAX_PATH.int32
+    when useWinUnicode:
+      var unused: WideCString = nil
+      var res = newWideCString("", bufsize)
+      while true:
+        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString("", L)
+          bufsize = L
+        else:
+          result = res$L
+          break
+    else:
+      var unused: cstring = nil
+      result = newString(bufsize)
+      while true:
+        var L = getFullPathNameA(filename, bufsize, result, unused)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          result = newString(L)
+          bufsize = L
+        else:
+          setLen(result, L)
+          break
+    # getFullPathName doesn't do case corrections, so we have to use this convoluted
+    # way of retrieving the true filename
+    for x in walkFiles(result.string):
+      result = x
+    if not existsFile(result) and not existsDir(result):
+      raise newException(OSError, "file '" & result & "' does not exist")
+  else:
+    # according to Posix we don't need to allocate space for result pathname.
+    # But we need to free return value with free(3).
+    var r = realpath(filename, nil)
+    if r.isNil:
+      raiseOSError(osLastError())
+    else:
+      result = $r
+      c_free(cast[pointer](r))
+
 type
   PathComponent* = enum   ## Enumeration specifying a path component.
     pcFile,               ## path refers to a file
@@ -1418,7 +1445,13 @@ type
     pcDir,                ## path refers to a directory
     pcLinkToDir           ## path refers to a symbolic link to a directory
 
-when defined(posix) and not defined(nimscript):
+proc getCurrentCompilerExe*(): string {.compileTime.} = discard
+  ## `getAppFilename` at CT; can be used to retrive the currently executing
+  ## Nim compiler from a Nim or nimscript program, or the nimble binary
+  ## inside a nimble program (likewise with other binaries built from
+  ## compiler API).
+
+when defined(posix) and not weirdTarget:
   proc getSymlinkFileKind(path: string): PathComponent =
     # Helper function.
     var s: Stat
@@ -1459,7 +1492,7 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
     for k, v in items(staticWalkDir(dir, relative)):
       yield (k, v)
   else:
-    when defined(nimscript):
+    when weirdTarget:
       for k, v in items(staticWalkDir(dir, relative)):
         yield (k, v)
     elif defined(windows):
@@ -1494,8 +1527,9 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
             var y = $x.d_name.cstring
           if y != "." and y != "..":
             var s: Stat
+            let path = dir / y
             if not relative:
-              y = dir / y
+              y = path
             var k = pcFile
 
             when defined(linux) or defined(macosx) or
@@ -1503,16 +1537,16 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
               if x.d_type != DT_UNKNOWN:
                 if x.d_type == DT_DIR: k = pcDir
                 if x.d_type == DT_LNK:
-                  if dirExists(y): k = pcLinkToDir
+                  if dirExists(path): k = pcLinkToDir
                   else: k = pcLinkToFile
                 yield (k, y)
                 continue
 
-            if lstat(y, s) < 0'i32: break
+            if lstat(path, s) < 0'i32: break
             if S_ISDIR(s.st_mode):
               k = pcDir
             elif S_ISLNK(s.st_mode):
-              k = getSymlinkFileKind(y)
+              k = getSymlinkFileKind(path)
             yield (k, y)
 
 iterator walkDirRec*(dir: string,
@@ -1953,7 +1987,7 @@ when defined(nimdoc):
     ##   else:
     ##     # Do something else!
 
-elif defined(nintendoswitch) or defined(nimscript):
+elif defined(nintendoswitch) or weirdTarget:
   proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
     raise newException(OSError, "paramStr is not implemented on Nintendo Switch")
 
@@ -2030,7 +2064,7 @@ when declared(paramCount) or defined(nimdoc):
     for i in 1..paramCount():
       result.add(paramStr(i))
 
-when not defined(nimscript) and (defined(freebsd) or defined(dragonfly)):
+when not weirdTarget and (defined(freebsd) or defined(dragonfly)):
   proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize,
               newp: pointer, newplen: csize): cint
        {.importc: "sysctl",header: """#include <sys/types.h>
@@ -2063,7 +2097,7 @@ when not defined(nimscript) and (defined(freebsd) or defined(dragonfly)):
         result.setLen(pathLength)
         break
 
-when not defined(nimscript) and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)):
+when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)):
   proc getApplAux(procPath: string): string =
     result = newString(256)
     var len = readlink(procPath, result, 256)
@@ -2072,7 +2106,7 @@ when not defined(nimscript) and (defined(linux) or defined(solaris) or defined(b
       len = readlink(procPath, result, len)
     setLen(result, len)
 
-when not (defined(windows) or defined(macosx) or defined(nimscript)):
+when not (defined(windows) or defined(macosx) or weirdTarget):
   proc getApplHeuristic(): string =
     when declared(paramStr):
       result = string(paramStr(0))
@@ -2117,7 +2151,8 @@ when defined(haiku):
       result = ""
 
 proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} =
-  ## Returns the filename of the application's executable.
+  ## Returns the filename of the application's executable. See also
+  ## `getCurrentCompilerExe`.
   ##
   ## This procedure will resolve symlinks.
 
@@ -2207,7 +2242,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
       close(f)
     else: raiseOSError(osLastError())
 
-when defined(Windows) or defined(nimscript):
+when defined(Windows) or weirdTarget:
   type
     DeviceId* = int32
     FileId* = int64
@@ -2287,6 +2322,12 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
       assert(path != "") # symlinks can't occur for file handles
       formalInfo.kind = getSymlinkFileKind(path)
 
+when defined(js):
+  when not declared(FileHandle):
+    type FileHandle = distinct int32
+  when not declared(File):
+    type File = object
+
 proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} =
   ## Retrieves file information for the file object represented by the given
   ## handle.
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index b2239b9c5..e7ab395ae 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -60,9 +60,6 @@ type
   Process* = ref ProcessObj ## represents an operating system process
 
 
-const poUseShell* {.deprecated.} = poUsePath
-  ## Deprecated alias for poUsePath.
-
 proc execProcess*(command: string,
                   workingDir: string = "",
                   args: openArray[string] = [],
@@ -229,7 +226,7 @@ proc execProcesses*(cmds: openArray[string],
                     {.rtl, extern: "nosp$1",
                     tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect]} =
   ## executes the commands `cmds` in parallel. Creates `n` processes
-  ## that execute in parallel. The highest return value of all processes
+  ## that execute in parallel. The highest (absolute) return value of all processes
   ## is returned. Runs `beforeRunEvent` before running each command.
 
   assert n > 0
@@ -335,19 +332,6 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
-proc select*(readfds: var seq[Process], timeout = 500): int
-  {.benign, deprecated.}
-  ## `select` with a sensible Nim interface. `timeout` is in milliseconds.
-  ## Specify -1 for no timeout. Returns the number of processes that are
-  ## ready to read from. The processes that are ready to be read from are
-  ## removed from `readfds`.
-  ##
-  ## **Warning**: This function may give unexpected or completely wrong
-  ## results on Windows.
-  ##
-  ## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
-  ## and so should not be used in newly written code.
-
 when not defined(useNimRtl):
   proc execProcess(command: string,
                    workingDir: string = "",
@@ -724,13 +708,6 @@ elif not defined(useNimRtl):
   proc isExitStatus(status: cint): bool =
     WIFEXITED(status) or WIFSIGNALED(status)
 
-  proc exitStatus(status: cint): cint =
-    if WIFSIGNALED(status):
-      # like the shell!
-      128 + WTERMSIG(status)
-    else:
-      WEXITSTATUS(status)
-
   proc envToCStringArray(t: StringTableRef): cstringArray =
     result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
     var i = 0
@@ -1054,7 +1031,7 @@ elif not defined(useNimRtl):
 
     proc waitForExit(p: Process, timeout: int = -1): int =
       if p.exitFlag:
-        return exitStatus(p.exitStatus)
+        return exitStatusLikeShell(p.exitStatus)
 
       if timeout == -1:
         var status: cint = 1
@@ -1109,7 +1086,7 @@ elif not defined(useNimRtl):
         finally:
           discard posix.close(kqFD)
 
-      result = exitStatus(p.exitStatus)
+      result = exitStatusLikeShell(p.exitStatus)
   else:
     import times
 
@@ -1142,7 +1119,7 @@ elif not defined(useNimRtl):
         s.tv_nsec = b.tv_nsec
 
       if p.exitFlag:
-        return exitStatus(p.exitStatus)
+        return exitStatusLikeShell(p.exitStatus)
 
       if timeout == -1:
         var status: cint = 1
@@ -1220,20 +1197,20 @@ elif not defined(useNimRtl):
             if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
               raiseOSError(osLastError())
 
-      result = exitStatus(p.exitStatus)
+      result = exitStatusLikeShell(p.exitStatus)
 
   proc peekExitCode(p: Process): int =
     var status = cint(0)
     result = -1
     if p.exitFlag:
-      return exitStatus(p.exitStatus)
+      return exitStatusLikeShell(p.exitStatus)
 
     var ret = waitpid(p.id, status, WNOHANG)
     if ret > 0:
       if isExitStatus(status):
         p.exitFlag = true
         p.exitStatus = status
-        result = exitStatus(status)
+        result = exitStatusLikeShell(status)
 
   proc createStream(stream: var Stream, handle: var FileHandle,
                     fileMode: FileMode) =
@@ -1265,7 +1242,7 @@ elif not defined(useNimRtl):
   proc execCmd(command: string): int =
     when defined(linux):
       let tmp = csystem(command)
-      result = if tmp == -1: tmp else: exitStatus(tmp)
+      result = if tmp == -1: tmp else: exitStatusLikeShell(tmp)
     else:
       result = csystem(command)
 
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index b991dd57f..106d59017 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -111,7 +111,7 @@
 ##     dict.writeConfig("config.ini")
 
 import
-  hashes, strutils, lexbase, streams, tables
+  strutils, lexbase, streams, tables
 
 include "system/inclrtl"
 
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index fe3d3186f..06f32f032 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -73,24 +73,6 @@ proc parseWord(s: string, i: int, w: var string,
       inc(result)
 
 when declared(os.paramCount):
-  proc quote(s: string): string =
-    if find(s, {' ', '\t'}) >= 0 and s.len > 0 and s[0] != '"':
-      if s[0] == '-':
-        result = newStringOfCap(s.len)
-        var i = parseWord(s, 0, result, {' ', '\t', ':', '='})
-        if i < s.len and s[i] in {':','='}:
-          result.add s[i]
-          inc i
-        result.add '"'
-        while i < s.len:
-          result.add s[i]
-          inc i
-        result.add '"'
-      else:
-        result = '"' & s & '"'
-    else:
-      result = s
-
   # we cannot provide this for NimRtl creation on Posix, because we can't
   # access the command line arguments then!
 
@@ -228,11 +210,12 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
 when declared(os.paramCount):
   proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
     ## retrieves the rest of the command line that has not been parsed yet.
-    var res = ""
-    for i in p.idx..<p.cmds.len:
-      if i > p.idx: res.add ' '
-      res.add quote(p.cmds[i])
-    result = res.TaintedString
+    result = p.cmds[p.idx .. ^1].quoteShellCommand.TaintedString
+
+  proc remainingArgs*(p: OptParser): seq[TaintedString] {.rtl, extern: "npo$1".} =
+    ## retrieves the rest of the command line that has not been parsed yet.
+    result = @[]
+    for i in p.idx..<p.cmds.len: result.add TaintedString(p.cmds[i])
 
 iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] =
   ## This is an convenience iterator for iterating over the given OptParser object.
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
index 51a70b6d1..9fd6cd2c7 100644
--- a/lib/pure/parseopt2.nim
+++ b/lib/pure/parseopt2.nim
@@ -55,15 +55,6 @@ proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
 
   result.cmd = @cmdline
 
-proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
-  ## Initalizes option parses with cmdline. Splits cmdline in on spaces
-  ## and calls initOptParser(openarray[string])
-  ## Do not use.
-  if cmdline == "": # backward compatibility
-    return initOptParser(@[])
-  else:
-    return initOptParser(cmdline.split)
-
 when not defined(createNimRtl):
   proc initOptParser*(): OptParser =
     ## Initializes option parser from current command line arguments.
@@ -112,10 +103,10 @@ proc next(p: var OptParser) =
     p.key = token
     p.val = ""
 
-proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecated.} =
-  ## Returns part of command line string that has not been parsed yet.
-  ## Do not use - does not correctly handle whitespace.
-  return p.cmd[p.pos..p.cmd.len-1].join(" ")
+proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1".} =
+  ## Returns the part of command line string that has not been parsed yet,
+  ## properly quoted.
+  return p.cmd[p.pos..p.cmd.len-1].quoteShellCommand
 
 type
   GetoptResult* = tuple[kind: CmdLineKind, key, val: TaintedString]
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index 20f02e815..f0961829b 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -11,7 +11,7 @@
 ## parser. It parses PostgreSQL syntax and the SQL ANSI standard.
 
 import
-  hashes, strutils, lexbase
+  strutils, lexbase
 
 # ------------------- scanner -------------------------------------------------
 
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index fb4bc19af..ba09347a2 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -7,9 +7,46 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains helpers for parsing tokens, numbers, identifiers, etc.
+## This module contains helpers for parsing tokens, numbers, integers, floats,
+## identifiers, etc.
 ##
 ## To unpack raw bytes look at the `streams <streams.html>`_ module.
+##
+##
+## .. code-block::
+##    import parseutils
+##
+##    let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
+##
+##    for log in logs:
+##      var res: string
+##      if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
+##        echo res & " - " & captureBetween(log, ' ', '_')
+##        # => 2019-01-10 - OK
+##
+##
+## .. code-block::
+##    import parseutils
+##    from strutils import Digits, parseInt
+##
+##    let userInput1 = "2019 school start"
+##    let userInput2 = "3 years back"
+##
+##    let startYear = input1[0..skipWhile(input1, Digits)-1] # 2019
+##    let yearsBack = input2[0..skipWhile(input2, Digits)-1] # 3
+##
+##    echo "Examination is in " & $(parseInt(startYear) + parseInt(yearsBack))
+##
+##
+## **See also:**
+## * `strutils module<strutils.html>`_ for combined and identical parsing proc's
+## * `json module<json.html>`_ for a JSON parser
+## * `parsecfg module<parsecfg.html>`_ for a configuration file parser
+## * `parsecsv module<parsecsv.html>`_ for a simple CSV (comma separated value) parser
+## * `parseopt module<parseopt.html>`_ for a command line parser
+## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
+## * `other parsers<lib.html#pure-libraries-parsers>`_ for other parsers
+
 
 {.deadCodeElim: on.}  # dce option deprecated
 
@@ -35,21 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
   ## proc is sensitive to the already existing value of ``number`` and will
   ## likely not do what you want unless you make sure ``number`` is zero. You
   ## can use this feature to *chain* calls, though the result int will quickly
-  ## overflow. Example:
-  ##
-  ## .. code-block:: nim
-  ##   var value = 0
-  ##   discard parseHex("0x38", value)
-  ##   assert value == 56
-  ##   discard parseHex("0x34", value)
-  ##   assert value == 56 * 256 + 52
-  ##   value = -1
-  ##   discard parseHex("0x38", value)
-  ##   assert value == -200
+  ## overflow.
   ##
   ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var value = 0
+    discard parseHex("0x38", value)
+    assert value == 56
+    discard parseHex("0x34", value)
+    assert value == 56 * 256 + 52
+    value = -1
+    discard parseHex("0x38", value)
+    assert value == -200
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -80,6 +116,11 @@ proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   ## If ``maxLen == 0`` the length of the octal number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var res: int
+    doAssert parseOct("12", res) == 2
+    doAssert res == 10
+    doAssert parseOct("9", res) == 0
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -95,7 +136,7 @@ proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
     inc(i)
   if foundDigit: result = i-start
 
-proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
+proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {.
   rtl, extern: "npuParseBin", noSideEffect.} =
   ## Parses an binary number and stores its value in ``number``. Returns
   ## the number of the parsed characters or 0 in case of an error.
@@ -103,6 +144,10 @@ proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   ## If ``maxLen == 0`` the length of the binary number has no upper bound.
   ## Else no more than ``start + maxLen`` characters are parsed, up to the
   ## length of the string.
+  runnableExamples:
+    var res: int
+    doAssert parseBin("010011100110100101101101", res) == 24
+    doAssert parseBin("3", res) == 0
   var i = start
   var foundDigit = false
   # get last index based on minimum `start + maxLen` or `s.len`
@@ -119,8 +164,16 @@ proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   if foundDigit: result = i-start
 
 proc parseIdent*(s: string, ident: var string, start = 0): int =
-  ## parses an identifier and stores it in ``ident``. Returns
+  ## Parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
+  runnableExamples:
+    var res: string
+    doAssert parseIdent("Hello World", res, 0) == 5
+    doAssert res == "Hello"
+    doAssert parseIdent("Hello World", res, 1) == 4
+    doAssert res == "ello"
+    doAssert parseIdent("Hello World", res, 6) == 5
+    doAssert res == "World"
   var i = start
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
@@ -129,8 +182,13 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     result = i-start
 
 proc parseIdent*(s: string, start = 0): string =
-  ## parses an identifier and returns it or an empty string in
+  ## Parses an identifier and returns it or an empty string in
   ## case of an error.
+  runnableExamples:
+    doAssert parseIdent("Hello World", 0) == "Hello"
+    doAssert parseIdent("Hello World", 1) == "ello"
+    doAssert parseIdent("Hello World", 5) == ""
+    doAssert parseIdent("Hello World", 6) == "World"
   result = ""
   var i = start
   if i < s.len and s[i] in IdentStartChars:
@@ -138,33 +196,35 @@ proc parseIdent*(s: string, start = 0): string =
     while i < s.len and s[i] in IdentChars: inc(i)
     result = substr(s, start, i-1)
 
-proc parseToken*(s: string, token: var string, validChars: set[char],
-                 start = 0): int {.inline, deprecated.} =
-  ## parses a token and stores it in ``token``. Returns
-  ## the number of the parsed characters or 0 in case of an error. A token
-  ## consists of the characters in `validChars`.
-  ##
-  ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead.
-  var i = start
-  while i < s.len and s[i] in validChars: inc(i)
-  result = i-start
-  token = substr(s, start, i-1)
-
 proc skipWhitespace*(s: string, start = 0): int {.inline.} =
-  ## skips the whitespace starting at ``s[start]``. Returns the number of
+  ## Skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
+  runnableExamples:
+    doAssert skipWhitespace("Hello World", 0) == 0
+    doAssert skipWhitespace(" Hello World", 0) == 1
+    doAssert skipWhitespace("Hello World", 5) == 1
+    doAssert skipWhitespace("Hello  World", 5) == 2
   while start+result < s.len and s[start+result] in Whitespace: inc(result)
 
 proc skip*(s, token: string, start = 0): int {.inline.} =
-  ## skips the `token` starting at ``s[start]``. Returns the length of `token`
+  ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
+  runnableExamples:
+    doAssert skip("2019-01-22", "2019", 0) == 4
+    doAssert skip("2019-01-22", "19", 0) == 0
+    doAssert skip("2019-01-22", "19", 2) == 2
+    doAssert skip("CAPlow", "CAP", 0) == 3
+    doAssert skip("CAPlow", "cap", 0) == 0
   while start+result < s.len and result < token.len and
       s[result+start] == token[result]:
     inc(result)
   if result != token.len: result = 0
 
 proc skipIgnoreCase*(s, token: string, start = 0): int =
-  ## same as `skip` but case is ignored for token matching.
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
   while start+result < s.len and result < token.len and
       toLower(s[result+start]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
@@ -173,24 +233,45 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
+    doAssert skipUntil("Hello World", {'W'}, 0) == 6
+    doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
   while start+result < s.len and s[result+start] notin until: inc(result)
 
 proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipUntil("Hello World", 'o', 0) == 4
+    doAssert skipUntil("Hello World", 'o', 4) == 0
+    doAssert skipUntil("Hello World", 'W', 0) == 6
+    doAssert skipUntil("Hello World", 'w', 0) == 11
   while start+result < s.len and s[result+start] != until: inc(result)
 
 proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
   ## Skips all characters while one char from the set `token` is found.
   ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipWhile("Hello World", {'H', 'e'}) == 2
+    doAssert skipWhile("Hello World", {'e'}) == 0
+    doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
   while start+result < s.len and s[result+start] in toSkip: inc(result)
 
 proc parseUntil*(s: string, token: var string, until: set[char],
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters notin `until`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
+    doAssert myToken == "lo "
   var i = start
   while i < s.len and s[i] notin until: inc(i)
   result = i-start
@@ -198,9 +279,17 @@ proc parseUntil*(s: string, token: var string, until: set[char],
 
 proc parseUntil*(s: string, token: var string, until: char,
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that is not the `until` character.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, 'W') == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, 'o') == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
+    doAssert myToken == "ll"
   var i = start
   while i < s.len and s[i] != until: inc(i)
   result = i-start
@@ -208,9 +297,15 @@ proc parseUntil*(s: string, token: var string, until: char,
 
 proc parseUntil*(s: string, token: var string, until: string,
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that comes before the `until`  token.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, "Wor") == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4
+    doAssert myToken == "llo "
   if until.len == 0:
     token.setLen(0)
     return 0
@@ -227,9 +322,15 @@ proc parseUntil*(s: string, token: var string, until: string,
 
 proc parseWhile*(s: string, token: var string, validChars: set[char],
                  start = 0): int {.inline.} =
-  ## parses a token and stores it in ``token``. Returns
+  ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters in `validChars`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0
+    doAssert myToken.len() == 0
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
+    doAssert myToken == "Wor"
   var i = start
   while i < s.len and s[i] in validChars: inc(i)
   result = i-start
@@ -238,12 +339,21 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
 proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
   ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
+  runnableExamples:
+    doAssert captureBetween("Hello World", 'e') == "llo World"
+    doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
+    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
   var i = skipUntil(s, first, start)+1+start
   result = ""
   discard s.parseUntil(result, if second == '\0': first else: second, i)
 
-{.push overflowChecks: on.}
-# this must be compiled with overflow checking turned on:
+proc integerOutOfRangeError() {.noinline.} =
+  raise newException(ValueError, "Parsed integer outside of valid range")
+
+# See #6752
+when defined(js):
+  {.push overflowChecks: off.}
+
 proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
   var
     sign: BiggestInt = -1
@@ -256,48 +366,67 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
     while i < s.len and s[i] in {'0'..'9'}:
-      b = b * 10 - (ord(s[i]) - ord('0'))
+      let c = ord(s[i]) - ord('0')
+      if b >= (low(BiggestInt) + c) div 10:
+        b = b * 10 - c
+      else:
+        integerOutOfRangeError()
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    b = b * sign
-    result = i - start
-{.pop.} # overflowChecks
+    if sign == -1 and b == low(BiggestInt):
+      integerOutOfRangeError()
+    else:
+      b = b * sign
+      result = i - start
+
+when defined(js):
+  {.pop.} # overflowChecks: off
 
 proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
-  rtl, extern: "npuParseBiggestInt", noSideEffect.} =
-  ## parses an integer starting at `start` and stores the value into `number`.
+  rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `OverflowError` is raised if an overflow occurs.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestInt
+    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert res == 9223372036854775807
   var res: BiggestInt
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
   result = rawParseInt(s, res, start)
-  number = res
+  if result != 0:
+    number = res
 
 proc parseInt*(s: string, number: var int, start = 0): int {.
-  rtl, extern: "npuParseInt", noSideEffect.} =
-  ## parses an integer starting at `start` and stores the value into `number`.
+  rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
-  ## `OverflowError` is raised if an overflow occurs.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("2019", res, 0) == 4
+    doAssert res == 2019
+    doAssert parseInt("2019", res, 2) == 2
+    doAssert res == 19
   var res: BiggestInt
   result = parseBiggestInt(s, res, start)
-  if (sizeof(int) <= 4) and
-      ((res < low(int)) or (res > high(int))):
-    raise newException(OverflowError, "overflow")
-  elif result != 0:
+  when sizeof(int) <= 4:
+    if res < low(int) or res > high(int):
+      integerOutOfRangeError()
+  if result != 0:
     number = int(res)
 
-proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
-  ## parses a natural number into ``b``. This cannot raise an overflow
+proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+  raises: [].}=
+  ## Parses a natural number into ``b``. This cannot raise an overflow
   ## error. ``high(int)`` is returned for an overflow.
   ## The number of processed character is returned.
   ## This is usually what you really want to use instead of `parseInt`:idx:.
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##   var res = 0
-  ##   discard parseSaturatedNatural("848", res)
-  ##   doAssert res == 848
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
   var i = start
   if i < s.len and s[i] == '+': inc(i)
   if i < s.len and s[i] in {'0'..'9'}:
@@ -312,12 +441,13 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     result = i - start
 
-# overflowChecks doesn't work with BiggestUInt
 proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
   var
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
     i = start
+  if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
+    integerOutOfRangeError()
   if i < s.len and s[i] == '+': inc(i) # Allow
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
@@ -325,56 +455,75 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
       prev = res
       res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
       if prev > res:
-        return 0 # overflowChecks emulation
+        integerOutOfRangeError()
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = res
     result = i - start
 
 proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
-  rtl, extern: "npuParseBiggestUInt", noSideEffect.} =
-  ## parses an unsigned integer starting at `start` and stores the value
+  rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer
-  ## or overflow detected.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestUInt
+    doAssert parseBiggestUInt("12", res, 0) == 2
+    doAssert res == 12
+    doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
+    doAssert res == 1111111111111111111'u64
   var res: BiggestUInt
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
   result = rawParseUInt(s, res, start)
-  number = res
+  if result != 0:
+    number = res
 
 proc parseUInt*(s: string, number: var uint, start = 0): int {.
-  rtl, extern: "npuParseUInt", noSideEffect.} =
-  ## parses an unsigned integer starting at `start` and stores the value
+  rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
   ## into `number`.
-  ## Result is the number of processed chars or 0 if there is no integer or
-  ## overflow detected.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: uint
+    doAssert parseUInt("3450", res) == 4
+    doAssert res == 3450
+    doAssert parseUInt("3450", res, 2) == 2
+    doAssert res == 50
   var res: BiggestUInt
   result = parseBiggestUInt(s, res, start)
   when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
     if res > 0xFFFF_FFFF'u64:
-      raise newException(OverflowError, "overflow")
+      integerOutOfRangeError()
   if result != 0:
     number = uint(res)
 
 proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
   magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if a parsing error
   ## occurred.
 
 proc parseFloat*(s: string, number: var float, start = 0): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
-  ## parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float starting at `start` and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there occurred a parsing
   ## error.
+  runnableExamples:
+    var res: float
+    doAssert parseFloat("32", res, 0) == 2
+    doAssert res == 32.0
+    doAssert parseFloat("32.57", res, 0) == 5
+    doAssert res == 32.57
+    doAssert parseFloat("32.57", res, 3) == 2
+    doAssert res == 57.00
   var bf: BiggestFloat
   result = parseBiggestFloat(s, bf, start)
   if result != 0:
     number = bf
 
 type
-  InterpolatedKind* = enum   ## describes for `interpolatedFragments`
+  InterpolatedKind* = enum   ## Describes for `interpolatedFragments`
                              ## which part of the interpolated string is
                              ## yielded; for example in "str$$$var${expr}"
     ikStr,                   ## ``str`` part of the interpolated string
@@ -490,4 +639,8 @@ when isMainModule:
   doAssert(parseSaturatedNatural("1_000_000", value) == 9)
   doAssert value == 1_000_000
 
+  var i64Value: int64
+  discard parseBiggestInt("9223372036854775807", i64Value)
+  doAssert i64Value == 9223372036854775807
+
 {.pop.}
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 0967f7983..953c5cdde 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -147,7 +147,7 @@ an HTML document contains.
 ]##
 
 import
-  hashes, strutils, lexbase, streams, unicode
+  strutils, lexbase, streams, unicode
 
 # the parser treats ``<br />`` as ``<br></br>``
 
diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim
index a33afefbd..ca869fd03 100644
--- a/lib/pure/pathnorm.nim
+++ b/lib/pure/pathnorm.nim
@@ -66,19 +66,27 @@ proc addNormalizePath*(x: string; result: var string; state: var int; dirSep = D
     if (state shr 1 == 0) and isSlash(x, b):
       result.add dirSep
       state = state or 1
-    elif result.len > (state and 1) and isDotDot(x, b):
-      var d = result.len
-      # f/..
-      while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}:
-        dec d
-      if d > 0: setLen(result, d-1)
+    elif isDotDot(x, b):
+      if (state shr 1) >= 1:
+        var d = result.len
+        # f/..
+        while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}:
+          dec d
+        if d > 0:
+          setLen(result, d-1)
+          dec state, 2
+      else:
+        if result.len > 0 and result[^1] notin {DirSep, AltSep}:
+          result.add dirSep
+        result.add substr(x, b[0], b[1])
     elif isDot(x, b):
       discard "discard the dot"
     elif b[1] >= b[0]:
       if result.len > 0 and result[^1] notin {DirSep, AltSep}:
         result.add dirSep
       result.add substr(x, b[0], b[1])
-    inc state, 2
+      inc state, 2
+  if result == "" and x != "": result = "."
 
 proc normalizePath*(path: string; dirSep = DirSep): string =
   ## Example:
@@ -89,7 +97,7 @@ proc normalizePath*(path: string; dirSep = DirSep): string =
   ##
   ## - Turns multiple slashes into single slashes.
   ## - Resolves '/foo/../bar' to '/bar'.
-  ## - Removes './' from the path.
+  ## - Removes './' from the path (but "foo/.." becomes ".")
   result = newStringOfCap(path.len)
   var state = 0
   addNormalizePath(path, result, state, dirSep)
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index c458d51eb..378ca6f87 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -14,6 +14,8 @@
 ##
 ## **Do not use this module for cryptographic purposes!**
 
+import algorithm                    #For upperBound
+
 include "system/inclrtl"
 {.push debugger:off.}
 
@@ -155,14 +157,48 @@ proc rand*[T](x: HSlice[T, T]): T =
   ## For a slice `a .. b` returns a value in the range `a .. b`.
   result = rand(state, x)
 
-proc rand*[T](r: var Rand; a: openArray[T]): T =
-  ## returns a random element from the openarray `a`.
+proc rand*[T](r: var Rand; a: openArray[T]): T {.deprecated.} =
+  ## Returns a random element from the openarray `a`.
+  ## **Deprecated since v0.20.0:** use ``sample`` instead.
   result = a[rand(r, a.low..a.high)]
 
-proc rand*[T](a: openArray[T]): T =
+proc rand*[T: SomeInteger](t: typedesc[T]): T =
+  ## Returns a random integer in the range `low(T)..high(T)`.
+  result = cast[T](state.next)
+
+proc rand*[T](a: openArray[T]): T {.deprecated.} =
   ## returns a random element from the openarray `a`.
+  ## **Deprecated since v0.20.0:** use ``sample`` instead.
+  result = a[rand(a.low..a.high)]
+
+proc sample*[T](r: var Rand; a: openArray[T]): T =
+  ## returns a random element from openArray ``a`` using state in ``r``.
+  result = a[r.rand(a.low..a.high)]
+
+proc sample*[T](a: openArray[T]): T =
+  ## returns a random element from openArray ``a`` using non-thread-safe state.
   result = a[rand(a.low..a.high)]
 
+proc sample*[T, U](r: var Rand; a: openArray[T], cdf: openArray[U]): T =
+  ## Sample one element from openArray ``a`` when it has cumulative distribution
+  ## function (CDF) ``cdf`` (not necessarily normalized, any type of elements
+  ## convertible to ``float``). Uses state in ``r``. E.g.:
+  ##
+  ## .. code-block:: nim
+  ##   let val = [ "a", "b", "c", "d" ]  # some values
+  ##   var cnt = [1, 2, 3, 4]            # histogram of counts
+  ##   echo r.sample(val, cnt.cumsummed) # echo a sample
+  assert(cdf.len == a.len)              # Two basic sanity checks.
+  assert(float(cdf[^1]) > 0.0)
+  #While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get
+  #awfully expensive even in debugging modes.
+  let u = r.rand(float(cdf[^1]))
+  a[cdf.upperBound(U(u))]
+
+proc sample*[T, U](a: openArray[T], cdf: openArray[U]): T =
+  ## Like ``sample(var Rand; openArray[T], openArray[U])``, but uses default
+  ## non-thread-safe state.
+  state.sample(a, cdf)
 
 proc initRand*(seed: int64): Rand =
   ## Creates a new ``Rand`` state from ``seed``.
@@ -191,8 +227,12 @@ when not defined(nimscript):
   proc randomize*() {.benign.} =
     ## Initializes the random number generator with a "random"
     ## number, i.e. a tickcount. Note: Does not work for NimScript.
-    let now = times.getTime()
-    randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond)
+    when defined(js):
+      let time = int64(times.epochTime() * 1_000_000_000)
+      randomize(time)
+    else:
+      let now = times.getTime()
+      randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond)
 
 {.pop.}
 
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
deleted file mode 100644
index e36803823..000000000
--- a/lib/pure/scgi.nim
+++ /dev/null
@@ -1,295 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Andreas Rumpf, Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements helper procs for SCGI applications. Example:
-##
-## .. code-block:: Nim
-##
-##    import strtabs, sockets, scgi
-##
-##    var counter = 0
-##    proc handleRequest(client: Socket, input: string,
-##                       headers: StringTableRef): bool {.procvar.} =
-##      inc(counter)
-##      client.writeStatusOkTextContent()
-##      client.send("Hello for the $#th time." % $counter & "\c\L")
-##      return false # do not stop processing
-##
-##    run(handleRequest)
-##
-## **Warning:** The API of this module is unstable, and therefore is subject
-## to change.
-##
-## **Warning:** This module only supports the old asynchronous interface.
-## You may wish to use the `asynchttpserver <asynchttpserver.html>`_
-## instead for web applications.
-
-include "system/inclrtl"
-
-import sockets, strutils, os, strtabs, asyncio
-
-type
-  ScgiError* = object of IOError ## the exception that is raised, if a SCGI error occurs
-
-proc raiseScgiError*(msg: string) {.noreturn.} =
-  ## raises an ScgiError exception with message `msg`.
-  var e: ref ScgiError
-  new(e)
-  e.msg = msg
-  raise e
-
-proc parseWord(inp: string, outp: var string, start: int): int =
-  result = start
-  while inp[result] != '\0': inc(result)
-  outp = substr(inp, start, result-1)
-
-proc parseHeaders(s: string, L: int): StringTableRef =
-  result = newStringTable()
-  var i = 0
-  while i < L:
-    var key, val: string
-    i = parseWord(s, key, i)+1
-    i = parseWord(s, val, i)+1
-    result[key] = val
-  if s[i] == ',': inc(i)
-  else: raiseScgiError("',' after netstring expected")
-
-proc recvChar(s: Socket): char =
-  var c: char
-  if recv(s, addr(c), sizeof(c)) == sizeof(c):
-    result = c
-
-type
-  ScgiState* = object of RootObj ## SCGI state object
-    server: Socket
-    bufLen: int
-    client*: Socket ## the client socket to send data to
-    headers*: StringTableRef ## the parsed headers
-    input*: string  ## the input buffer
-
-
-  # Async
-
-  ClientMode = enum
-    ClientReadChar, ClientReadHeaders, ClientReadContent
-
-  AsyncClient = ref object
-    c: AsyncSocket
-    mode: ClientMode
-    dataLen: int
-    headers: StringTableRef ## the parsed headers
-    input: string  ## the input buffer
-
-  AsyncScgiStateObj = object
-    handleRequest: proc (client: AsyncSocket,
-                         input: string,
-                         headers: StringTableRef) {.closure, gcsafe.}
-    asyncServer: AsyncSocket
-    disp: Dispatcher
-  AsyncScgiState* = ref AsyncScgiStateObj
-
-proc recvBuffer(s: var ScgiState, L: int) =
-  if L > s.bufLen:
-    s.bufLen = L
-    s.input = newString(L)
-  if L > 0 and recv(s.client, cstring(s.input), L) != L:
-    raiseScgiError("could not read all data")
-  setLen(s.input, L)
-
-proc open*(s: var ScgiState, port = Port(4000), address = "127.0.0.1",
-           reuseAddr = false) =
-  ## opens a connection.
-  s.bufLen = 4000
-  s.input = newString(s.bufLen) # will be reused
-
-  s.server = socket()
-  if s.server == invalidSocket: raiseOSError(osLastError())
-  new(s.client) # Initialise s.client for `next`
-  if s.server == invalidSocket: raiseScgiError("could not open socket")
-  #s.server.connect(connectionName, port)
-  if reuseAddr:
-    s.server.setSockOpt(OptReuseAddr, true)
-  bindAddr(s.server, port, address)
-  listen(s.server)
-
-proc close*(s: var ScgiState) =
-  ## closes the connection.
-  s.server.close()
-
-proc next*(s: var ScgiState, timeout: int = -1): bool =
-  ## proceed to the first/next request. Waits ``timeout`` milliseconds for a
-  ## request, if ``timeout`` is `-1` then this function will never time out.
-  ## Returns `true` if a new request has been processed.
-  var rsocks = @[s.server]
-  if select(rsocks, timeout) == 1 and rsocks.len == 1:
-    new(s.client)
-    accept(s.server, s.client)
-    var L = 0
-    while true:
-      var d = s.client.recvChar()
-      if d == '\0':
-        s.client.close()
-        return false
-      if d notin strutils.Digits:
-        if d != ':': raiseScgiError("':' after length expected")
-        break
-      L = L * 10 + ord(d) - ord('0')
-    recvBuffer(s, L+1)
-    s.headers = parseHeaders(s.input, L)
-    if s.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected")
-    L = parseInt(s.headers.getOrDefault("CONTENT_LENGTH"))
-    recvBuffer(s, L)
-    return true
-
-proc writeStatusOkTextContent*(c: Socket, contentType = "text/html") =
-  ## sends the following string to the socket `c`::
-  ##
-  ##   Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
-  ##
-  ## You should send this before sending your HTML page, for example.
-  c.send("Status: 200 OK\r\L" &
-         "Content-Type: $1\r\L\r\L" % contentType)
-
-proc run*(handleRequest: proc (client: Socket, input: string,
-                               headers: StringTableRef): bool {.nimcall,gcsafe.},
-          port = Port(4000)) =
-  ## encapsulates the SCGI object and main loop.
-  var s: ScgiState
-  s.open(port)
-  var stop = false
-  while not stop:
-    if next(s):
-      stop = handleRequest(s.client, s.input, s.headers)
-      s.client.close()
-  s.close()
-
-# -- AsyncIO start
-
-proc recvBufferAsync(client: AsyncClient, L: int): ReadLineResult =
-  result = ReadPartialLine
-  var data = ""
-  if L < 1:
-    raiseScgiError("Cannot read negative or zero length: " & $L)
-  let ret = recvAsync(client.c, data, L)
-  if ret == 0 and data == "":
-    client.c.close()
-    return ReadDisconnected
-  if ret == -1:
-    return ReadNone # No more data available
-  client.input.add(data)
-  if ret == L:
-    return ReadFullLine
-
-proc checkCloseSocket(client: AsyncClient) =
-  if not client.c.isClosed:
-    if client.c.isSendDataBuffered:
-      client.c.setHandleWrite do (s: AsyncSocket):
-        if not s.isClosed and not s.isSendDataBuffered:
-          s.close()
-          s.delHandleWrite()
-    else: client.c.close()
-
-proc handleClientRead(client: AsyncClient, s: AsyncScgiState) =
-  case client.mode
-  of ClientReadChar:
-    while true:
-      var d = ""
-      let ret = client.c.recvAsync(d, 1)
-      if d == "" and ret == 0:
-        # Disconnected
-        client.c.close()
-        return
-      if ret == -1:
-        return # No more data available
-      if d[0] notin strutils.Digits:
-        if d[0] != ':': raiseScgiError("':' after length expected")
-        break
-      client.dataLen = client.dataLen * 10 + ord(d[0]) - ord('0')
-    client.mode = ClientReadHeaders
-    handleClientRead(client, s) # Allow progression
-  of ClientReadHeaders:
-    let ret = recvBufferAsync(client, (client.dataLen+1)-client.input.len)
-    case ret
-    of ReadFullLine:
-      client.headers = parseHeaders(client.input, client.input.len-1)
-      if client.headers.getOrDefault("SCGI") != "1": raiseScgiError("SCGI Version 1 expected")
-      client.input = "" # For next part
-
-      let contentLen = parseInt(client.headers.getOrDefault("CONTENT_LENGTH"))
-      if contentLen > 0:
-        client.mode = ClientReadContent
-      else:
-        s.handleRequest(client.c, client.input, client.headers)
-        checkCloseSocket(client)
-    of ReadPartialLine, ReadDisconnected, ReadNone: return
-  of ClientReadContent:
-    let L = parseInt(client.headers.getOrDefault("CONTENT_LENGTH")) -
-               client.input.len
-    if L > 0:
-      let ret = recvBufferAsync(client, L)
-      case ret
-      of ReadFullLine:
-        s.handleRequest(client.c, client.input, client.headers)
-        checkCloseSocket(client)
-      of ReadPartialLine, ReadDisconnected, ReadNone: return
-    else:
-      s.handleRequest(client.c, client.input, client.headers)
-      checkCloseSocket(client)
-
-proc handleAccept(sock: AsyncSocket, s: AsyncScgiState) =
-  var client: AsyncSocket
-  new(client)
-  accept(s.asyncServer, client)
-  var asyncClient = AsyncClient(c: client, mode: ClientReadChar, dataLen: 0,
-                                 headers: newStringTable(), input: "")
-  client.handleRead =
-    proc (sock: AsyncSocket) =
-      handleClientRead(asyncClient, s)
-  s.disp.register(client)
-
-proc open*(handleRequest: proc (client: AsyncSocket,
-                                input: string, headers: StringTableRef) {.
-                                closure, gcsafe.},
-           port = Port(4000), address = "127.0.0.1",
-           reuseAddr = false): AsyncScgiState =
-  ## Creates an ``AsyncScgiState`` object which serves as a SCGI server.
-  ##
-  ## After the execution of ``handleRequest`` the client socket will be closed
-  ## automatically unless it has already been closed.
-  var cres: AsyncScgiState
-  new(cres)
-  cres.asyncServer = asyncSocket()
-  cres.asyncServer.handleAccept = proc (s: AsyncSocket) = handleAccept(s, cres)
-  if reuseAddr:
-    cres.asyncServer.setSockOpt(OptReuseAddr, true)
-  bindAddr(cres.asyncServer, port, address)
-  listen(cres.asyncServer)
-  cres.handleRequest = handleRequest
-  result = cres
-
-proc register*(d: Dispatcher, s: AsyncScgiState): Delegate {.discardable.} =
-  ## Registers ``s`` with dispatcher ``d``.
-  result = d.register(s.asyncServer)
-  s.disp = d
-
-proc close*(s: AsyncScgiState) =
-  ## Closes the ``AsyncScgiState``.
-  s.asyncServer.close()
-
-when false:
-  var counter = 0
-  proc handleRequest(client: Socket, input: string,
-                     headers: StringTableRef): bool {.procvar.} =
-    inc(counter)
-    client.writeStatusOkTextContent()
-    client.send("Hello for the $#th time." % $counter & "\c\L")
-    return false # do not stop processing
-
-  run(handleRequest)
-
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index e4c2b2124..b9c834127 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -239,6 +239,9 @@ else:
     proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
       result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
 
+    proc reallocSharedArray[T](sa: ptr SharedArray[T], nsize: int): ptr SharedArray[T] =
+      result = cast[ptr SharedArray[T]](reallocShared(sa, sizeof(T) * nsize))
+
     proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
       deallocShared(cast[pointer](sa))
   type
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index b0ac62525..10de86e9f 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -146,12 +146,12 @@ proc writeLine*(s: Stream, args: varargs[string, `$`]) =
   for str in args: write(s, str)
   write(s, "\n")
 
-proc read[T](s: Stream, result: var T) =
+proc read*[T](s: Stream, result: var T) =
   ## generic read procedure. Reads `result` from the stream `s`.
   if readData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
-proc peek[T](s: Stream, result: var T) =
+proc peek*[T](s: Stream, result: var T) =
   ## generic peek procedure. Peeks `result` from the stream `s`.
   if peekData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
@@ -271,7 +271,7 @@ proc peekStr*(s: Stream, length: int): TaintedString =
 proc readLine*(s: Stream, line: var TaintedString): bool =
   ## reads a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
-  ## A line of text may be delimited by ```LF`` or ``CRLF``.
+  ## A line of text may be delimited by ``LF`` or ``CRLF``.
   ## The newline character(s) are not part of the returned string.
   ## Returns ``false`` if the end of the file has been reached, ``true``
   ## otherwise. If ``false`` is returned `line` contains no new data.
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index d8a23286a..7bafe1675 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -12,6 +12,34 @@
 ## style-insensitive mode. An efficient string substitution operator  ``%``
 ## for the string table is also provided.
 
+runnableExamples:
+
+  var t = newStringTable()
+  t["name"] = "John"
+  t["city"] = "Monaco"
+  doAssert t.len == 2
+  doAssert t.hasKey "name"
+  doAssert "name" in t
+
+## String tables can be created from a table constructor:
+
+runnableExamples:
+  var t = {"name": "John", "city": "Monaco"}.newStringTable
+
+
+## When using the style insensitive mode ``modeStyleInsensitive``, 
+## all letters are compared case insensitively within the ASCII range 
+## and underscores are ignored.
+
+runnableExamples:
+
+  var x = newStringTable(modeStyleInsensitive)
+  x["first_name"] = "John"
+  x["LastName"] = "Doe"
+
+  doAssert x["firstName"] == "John"
+  doAssert x["last_name"] == "Doe"
+
 import
   hashes, strutils
 
@@ -214,6 +242,9 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
 proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
   rtlFunc, extern: "nstFormat".} =
   ## The `%` operator for string tables.
+  runnableExamples:
+    var t = {"name": "John", "city": "Monaco"}.newStringTable
+    doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
   const
     PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
   result = ""
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 4d0fe800e..8385eb24e 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -7,11 +7,70 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains various string utility routines.
-## See the module `re <re.html>`_ for regular expression support.
-## See the module `pegs <pegs.html>`_ for PEG support.
+## The system module defines several common functions for working with strings,
+## such as:
+## * ``$`` for converting other data-types to strings
+## * ``&`` for string concatenation
+## * ``add`` for adding a new character or a string to the existing one
+## * ``in`` (alias for ``contains``) and ``notin`` for checking if a character
+##   is in a string
+##
+## This module builds upon that, providing additional functionality in form of
+## procedures, iterators and templates for strings.
+##
+## .. code-block::
+##   import strutils
+##
+##   let
+##     numbers = @[867, 5309]
+##     multiLineString = "first line\nsecond line\nthird line"
+##
+##   let jenny = numbers.join("-")
+##   assert jenny == "867-5309"
+##
+##   assert splitLines(multiLineString) ==
+##          @["first line", "second line", "third line"]
+##   assert split(multiLineString) == @["first", "line", "second",
+##                                      "line", "third", "line"]
+##   assert indent(multiLineString, 4) ==
+##          "    first line\n    second line\n    third line"
+##   assert 'z'.repeat(5) == "zzzzz"
+##
+## The chaining of functions is possible thanks to the
+## `method call syntax<manual.html#procedures-method-call-syntax>`_:
+##
+## .. code-block::
+##   import strutils
+##   from sequtils import map
+##
+##   let jenny = "867-5309"
+##   assert jenny.split('-').map(parseInt) == @[867, 5309]
+##
+##   assert "Beetlejuice".indent(1).repeat(3).strip ==
+##          "Beetlejuice Beetlejuice Beetlejuice"
+##
 ## This module is available for the `JavaScript target
 ## <backends.html#the-javascript-target>`_.
+##
+## ----
+##
+## **See also:**
+## * `strformat module<strformat.html>`_ for string interpolation and formatting
+## * `unicode module<unicode.html>`_ for Unicode UTF-8 handling
+## * `sequtils module<collections/sequtils.html>`_ for operations on container
+##   types (including strings)
+## * `parseutils module<parseutils.html>`_ for lower-level parsing of tokens,
+##   numbers, identifiers, etc.
+## * `parseopt module<parseopt.html>`_ for command-line parsing
+## * `strtabs module<strtabs.html>`_ for efficient hash tables
+##   (dictionaries, in some programming languages) mapping from strings to strings
+## * `pegs module<pegs.html>`_ for PEG (Parsing Expression Grammar) support
+## * `ropes module<ropes.html>`_ for rope data type, which can represent very
+##   long strings efficiently
+## * `re module<re.html>`_ for regular expression (regex) support
+## * `strscans<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which offer
+##   easier substring extraction than regular expressions
+
 
 import parseutils
 from math import pow, floor, log10
@@ -38,7 +97,8 @@ else:
 
 const
   Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
-    ## All the characters that count as whitespace.
+    ## All the characters that count as whitespace (space, tab, vertical tab,
+    ## carriage return, new line, form feed)
 
   Letters* = {'A'..'Z', 'a'..'z'}
     ## the set of letters
@@ -56,14 +116,15 @@ const
     ## the set of characters an identifier can start with
 
   NewLines* = {'\13', '\10'}
-    ## the set of characters a newline terminator can start with
+    ## the set of characters a newline terminator can start with (carriage
+    ## return, line feed)
 
   AllChars* = {'\x00'..'\xFF'}
     ## A set with all the possible characters.
     ##
     ## Not very useful by its own, you can use it to create *inverted* sets to
-    ## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
-    ## characters in strings.  Example:
+    ## make the `find proc<#find,string,set[char],Natural,int>`_
+    ## find **invalid** characters in strings. Example:
     ##
     ## .. code-block:: nim
     ##   let invalid = AllChars - Digits
@@ -72,9 +133,10 @@ const
 
 proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsAlphaAsciiChar".}=
-  ## Checks whether or not `c` is alphabetical.
+  ## Checks whether or not character `c` is alphabetical.
   ##
   ## This checks a-z, A-Z ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
   runnableExamples:
     doAssert isAlphaAscii('e') == true
     doAssert isAlphaAscii('E') == true
@@ -108,6 +170,7 @@ proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar,
   runnableExamples:
     doAssert isSpaceAscii('n') == false
     doAssert isSpaceAscii(' ') == true
+    doAssert isSpaceAscii('\t') == true
   return c in Whitespace
 
 proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
@@ -115,6 +178,10 @@ proc isLowerAscii*(c: char): bool {.noSideEffect, procvar,
   ## Checks whether or not `c` is a lower case character.
   ##
   ## This checks ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toLowerAscii proc<#toLowerAscii,char>`_
   runnableExamples:
     doAssert isLowerAscii('e') == true
     doAssert isLowerAscii('E') == false
@@ -126,138 +193,28 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
   ## Checks whether or not `c` is an upper case character.
   ##
   ## This checks ASCII characters only.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toUpperAscii proc<#toUpperAscii,char>`_
   runnableExamples:
     doAssert isUpperAscii('e') == false
     doAssert isUpperAscii('E') == true
     doAssert isUpperAscii('7') == false
   return c in {'A'..'Z'}
 
-template isImpl(call) =
-  if s.len == 0: return false
-  result = true
-  for c in s:
-    if not call(c): return false
-
-proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaAsciiStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is alphabetical.
-  ##
-  ## This checks a-z, A-Z ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## alphabetic and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isAlphaAscii("fooBar") == true
-    doAssert isAlphaAscii("fooBar1") == false
-    doAssert isAlphaAscii("foo Bar") == false
-  isImpl isAlphaAscii
-
-proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsAlphaNumericStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is alphanumeric.
-  ##
-  ## This checks a-z, A-Z, 0-9 ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## alpanumeric and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isAlphaNumeric("fooBar") == true
-    doAssert isAlphaNumeric("fooBar") == true
-    doAssert isAlphaNumeric("foo Bar") == false
-  isImpl isAlphaNumeric
-
-proc isDigit*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsDigitStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is a numeric value.
-  ##
-  ## This checks 0-9 ASCII characters only.
-  ## Returns true if all characters in `s` are
-  ## numeric and there is at least one character
-  ## in `s`.
-  runnableExamples:
-    doAssert isDigit("1908") == true
-    doAssert isDigit("fooBar1") == false
-  isImpl isDigit
-
-proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
-  rtl, extern: "nsuIsSpaceAsciiStr",
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether or not `s` is completely whitespace.
-  ##
-  ## Returns true if all characters in `s` are whitespace
-  ## characters and there is at least one character in `s`.
-  runnableExamples:
-    doAssert isSpaceAscii("   ") == true
-    doAssert isSpaceAscii("") == false
-  isImpl isSpaceAscii
-
-template isCaseImpl(s, charProc, skipNonAlpha) =
-  var hasAtleastOneAlphaChar = false
-  if s.len == 0: return false
-  for c in s:
-    if skipNonAlpha:
-      var charIsAlpha = c.isAlphaAscii()
-      if not hasAtleastOneAlphaChar:
-        hasAtleastOneAlphaChar = charIsAlpha
-      if charIsAlpha and (not charProc(c)):
-        return false
-    else:
-      if not charProc(c):
-        return false
-  return if skipNonAlpha: hasAtleastOneAlphaChar else: true
-
-proc isLowerAscii*(s: string, skipNonAlpha: bool): bool {.
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether ``s`` is lower case.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## characters in ``s`` are lower case.  Returns false if none of the
-  ## characters in ``s`` are alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all characters
-  ## in ``s`` are alphabetical and lower case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  runnableExamples:
-    doAssert isLowerAscii("1foobar", false) == false
-    doAssert isLowerAscii("1foobar", true) == true
-    doAssert isLowerAscii("1fooBar", true) == false
-  isCaseImpl(s, isLowerAscii, skipNonAlpha)
-
-proc isUpperAscii*(s: string, skipNonAlpha: bool): bool {.
-  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
-  ## Checks whether ``s`` is upper case.
-  ##
-  ## This checks ASCII characters only.
-  ##
-  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## characters in ``s`` are upper case.  Returns false if none of the
-  ## characters in ``s`` are alphabetical.
-  ##
-  ## If ``skipNonAlpha`` is false, returns true only if all characters
-  ## in ``s`` are alphabetical and upper case.
-  ##
-  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
-  ## an empty string.
-  runnableExamples:
-    doAssert isUpperAscii("1FOO", false) == false
-    doAssert isUpperAscii("1FOO", true) == true
-    doAssert isUpperAscii("1Foo", true) == false
-  isCaseImpl(s, isUpperAscii, skipNonAlpha)
 
 proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiChar".} =
-  ## Returns the lower case version of ``c``.
+  ## Returns the lower case version of character ``c``.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
   ## <unicode.html#toLower>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `isLowerAscii proc<#isLowerAscii,char>`_
+  ## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string
   runnableExamples:
     doAssert toLowerAscii('A') == 'a'
     doAssert toLowerAscii('e') == 'e'
@@ -273,22 +230,30 @@ template toImpl(call) =
 
 proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiStr".} =
-  ## Converts `s` into lower case.
+  ## Converts string `s` into lower case.
   ##
   ## This works only for the letters ``A-Z``. See `unicode.toLower
   ## <unicode.html#toLower>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `normalize proc<#normalize,string>`_
   runnableExamples:
     doAssert toLowerAscii("FooBar!") == "foobar!"
   toImpl toLowerAscii
 
 proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToUpperAsciiChar".} =
-  ## Converts `c` into upper case.
+  ## Converts character `c` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
   ## <unicode.html#toUpper>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `isLowerAscii proc<#isLowerAscii,char>`_
+  ## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string
+  ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
   runnableExamples:
     doAssert toUpperAscii('a') == 'A'
     doAssert toUpperAscii('E') == 'E'
@@ -299,20 +264,27 @@ proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
 
 proc toUpperAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuToUpperAsciiStr".} =
-  ## Converts `s` into upper case.
+  ## Converts string `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.  See `unicode.toUpper
   ## <unicode.html#toUpper>`_ for a version that works for any Unicode
   ## character.
+  ##
+  ## See also:
+  ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
   runnableExamples:
     doAssert toUpperAscii("FooBar!") == "FOOBAR!"
   toImpl toUpperAscii
 
 proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuCapitalizeAscii".} =
-  ## Converts the first character of `s` into upper case.
+  ## Converts the first character of string `s` into upper case.
   ##
   ## This works only for the letters ``A-Z``.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  ##
+  ## See also:
+  ## * `toUpperAscii proc<#toUpperAscii,char>`_
   runnableExamples:
     doAssert capitalizeAscii("foo") == "Foo"
     doAssert capitalizeAscii("-bar") == "-bar"
@@ -325,6 +297,9 @@ proc normalize*(s: string): string {.noSideEffect, procvar,
   ##
   ## That means to convert it to lower case and remove any '_'. This
   ## should NOT be used to normalize Nim identifier names.
+  ##
+  ## See also:
+  ## * `toLowerAscii proc<#toLowerAscii,string>`_
   runnableExamples:
     doAssert normalize("Foo_bar") == "foobar"
     doAssert normalize("Foo Bar") == "foo bar"
@@ -343,9 +318,9 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
   rtl, extern: "nsuCmpIgnoreCase", procvar.} =
   ## Compares two strings in a case insensitive manner. Returns:
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
+  ## | 0 if a == b
+  ## | < 0 if a < b
+  ## | > 0 if a > b
   runnableExamples:
     doAssert cmpIgnoreCase("FooBar", "foobar") == 0
     doAssert cmpIgnoreCase("bar", "Foo") < 0
@@ -365,12 +340,14 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
   rtl, extern: "nsuCmpIgnoreStyle", procvar.} =
   ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
   ## is just optimized to not allocate temporary strings. This should
-  ## NOT be used to compare Nim identifier names. use `macros.eqIdent`
-  ## for that. Returns:
+  ## NOT be used to compare Nim identifier names.
+  ## Use `macros.eqIdent<macros.html#eqIdent,string,string>`_ for that.
+  ##
+  ## Returns:
   ##
-  ## | 0 iff a == b
-  ## | < 0 iff a < b
-  ## | > 0 iff a > b
+  ## | 0 if a == b
+  ## | < 0 if a < b
+  ## | > 0 if a > b
   runnableExamples:
     doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
     doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
@@ -394,51 +371,8 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
     inc i
     inc j
 
-proc strip*(s: string, leading = true, trailing = true,
-            chars: set[char] = Whitespace): string
-  {.noSideEffect, rtl, extern: "nsuStrip".} =
-  ## Strips leading or trailing `chars` from `s` and returns
-  ## the resulting string.
-  ##
-  ## If `leading` is true, leading `chars` are stripped.
-  ## If `trailing` is true, trailing `chars` are stripped.
-  ## If both are false, the string is returned unchanged.
-  runnableExamples:
-    doAssert " vhellov ".strip().strip(trailing = false, chars = {'v'}) == "hellov"
-  var
-    first = 0
-    last = len(s)-1
-  if leading:
-    while first <= last and s[first] in chars: inc(first)
-  if trailing:
-    while last >= 0 and s[last] in chars: dec(last)
-  result = substr(s, first, last)
-
-proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
-  ## Converts a character `c` to its octal representation.
-  ##
-  ## The resulting string may not have a leading zero. Its length is always
-  ## exactly 3.
-  runnableExamples:
-    doAssert toOctal('!') == "041"
-  result = newString(3)
-  var val = ord(c)
-  for i in countdown(2, 0):
-    result[i] = chr(val mod 8 + ord('0'))
-    val = val div 8
 
-proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
-                                      extern: "nsuIsNilOrEmpty",
-                                      deprecated: "use 'x.len == 0' instead".} =
-  ## Checks if `s` is nil or empty.
-  result = len(s) == 0
-
-proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
-  ## Checks if `s` is nil or consists entirely of whitespace characters.
-  result = true
-  for c in s:
-    if not c.isSpaceAscii():
-      return false
+# --------- Private templates for different split separators -----------
 
 proc substrEq(s: string, pos: int, substr: string): bool =
   var i = 0
@@ -447,8 +381,6 @@ proc substrEq(s: string, pos: int, substr: string): bool =
     inc i
   return i == length
 
-# --------- Private templates for different split separators -----------
-
 template stringHasSep(s: string, index: int, seps: set[char]): bool =
   s[index] in seps
 
@@ -459,7 +391,7 @@ template stringHasSep(s: string, index: int, sep: string): bool =
   s.substrEq(index, sep)
 
 template splitCommon(s, sep, maxsplit, sepLen) =
-  ## Common code for split procedures
+  ## Common code for split procs
   var last = 0
   var splits = maxsplit
 
@@ -487,6 +419,42 @@ template oldSplit(s, seps, maxsplit) =
       if splits == 0: break
       dec(splits)
 
+template accResult(iter: untyped) =
+  result = @[]
+  for x in iter: add(result, x)
+
+
+iterator split*(s: string, sep: char, maxsplit: int = -1): string =
+  ## Splits the string `s` into substrings using a single separator.
+  ##
+  ## Substrings are separated by the character `sep`.
+  ## The code:
+  ##
+  ## .. code-block:: nim
+  ##   for word in split(";;this;is;an;;example;;;", ';'):
+  ##     writeLine(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block::
+  ##   ""
+  ##   ""
+  ##   "this"
+  ##   "is"
+  ##   "an"
+  ##   ""
+  ##   "example"
+  ##   ""
+  ##   ""
+  ##   ""
+  ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,char,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,char,int>`_
+  splitCommon(s, sep, maxsplit, 1)
+
 iterator split*(s: string, seps: set[char] = Whitespace,
                 maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a group of separators.
@@ -529,79 +497,13 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   ##   "08"
   ##   "08.398990"
   ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,set[char],int>`_
   splitCommon(s, seps, maxsplit, 1)
 
-iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
-  ## Splits the string ``s`` at whitespace stripping leading and trailing
-  ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
-  ## no more than ``maxsplit`` splits is made.
-  ##
-  ## The following code:
-  ##
-  ## .. code-block:: nim
-  ##   let s = "  foo \t bar  baz  "
-  ##   for ms in [-1, 1, 2, 3]:
-  ##     echo "------ maxsplit = ", ms, ":"
-  ##     for item in s.splitWhitespace(maxsplit=ms):
-  ##       echo '"', item, '"'
-  ##
-  ## ...results in:
-  ##
-  ## .. code-block::
-  ##   ------ maxsplit = -1:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz"
-  ##   ------ maxsplit = 1:
-  ##   "foo"
-  ##   "bar  baz  "
-  ##   ------ maxsplit = 2:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz  "
-  ##   ------ maxsplit = 3:
-  ##   "foo"
-  ##   "bar"
-  ##   "baz"
-  ##
-  oldSplit(s, Whitespace, maxsplit)
-
-template accResult(iter: untyped) =
-  result = @[]
-  for x in iter: add(result, x)
-
-proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
-  rtl, extern: "nsuSplitWhitespace".} =
-  ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
-  ## iterator, but is a proc that returns a sequence of substrings.
-  accResult(splitWhitespace(s, maxsplit))
-
-iterator split*(s: string, sep: char, maxsplit: int = -1): string =
-  ## Splits the string `s` into substrings using a single separator.
-  ##
-  ## Substrings are separated by the character `sep`.
-  ## The code:
-  ##
-  ## .. code-block:: nim
-  ##   for word in split(";;this;is;an;;example;;;", ';'):
-  ##     writeLine(stdout, word)
-  ##
-  ## Results in:
-  ##
-  ## .. code-block::
-  ##   ""
-  ##   ""
-  ##   "this"
-  ##   "is"
-  ##   "an"
-  ##   ""
-  ##   "example"
-  ##   ""
-  ##   ""
-  ##   ""
-  ##
-  splitCommon(s, sep, maxsplit, 1)
-
 iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Splits the string `s` into substrings using a string separator.
   ##
@@ -619,8 +521,14 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ##   "is"
   ##   "corrupted"
   ##
+  ## See also:
+  ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `split proc<#split,string,string,int>`_
   splitCommon(s, sep, maxsplit, sep.len)
 
+
 template rsplitCommon(s, sep, maxsplit, sepLen) =
   ## Common code for rsplit functions
   var
@@ -645,14 +553,14 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
     dec(first)
     last = first
 
-iterator rsplit*(s: string, seps: set[char] = Whitespace,
+iterator rsplit*(s: string, sep: char,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
   ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
-  ##   for piece in "foo bar".rsplit(WhiteSpace):
+  ##   for piece in "foo:bar".rsplit(':'):
   ##     echo piece
   ##
   ## Results in:
@@ -661,17 +569,23 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the set of chars `seps`
-  rsplitCommon(s, seps, maxsplit, 1)
+  ## Substrings are separated from the right by the char `sep`.
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,char,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,char,int>`_
+  rsplitCommon(s, sep, maxsplit, 1)
 
-iterator rsplit*(s: string, sep: char,
+iterator rsplit*(s: string, seps: set[char] = Whitespace,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
   ## <#split.i,string,char,int>`_ except in reverse order.
   ##
   ## .. code-block:: nim
-  ##   for piece in "foo:bar".rsplit(':'):
+  ##   for piece in "foo bar".rsplit(WhiteSpace):
   ##     echo piece
   ##
   ## Results in:
@@ -680,8 +594,14 @@ iterator rsplit*(s: string, sep: char,
   ##   "bar"
   ##   "foo"
   ##
-  ## Substrings are separated from the right by the char `sep`
-  rsplitCommon(s, sep, maxsplit, 1)
+  ## Substrings are separated from the right by the set of chars `seps`
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,set[char],int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,set[char],int>`_
+  rsplitCommon(s, seps, maxsplit, 1)
 
 iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
                  keepSeparators: bool = false): string =
@@ -700,6 +620,12 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
   ##   "foo"
   ##
   ## Substrings are separated from the right by the string `sep`
+  ##
+  ## See also:
+  ## * `split iterator<#split.i,string,string,int>`_
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `rsplit proc<#rsplit,string,string,int>`_
   rsplitCommon(s, sep, maxsplit, sep.len)
 
 iterator splitLines*(s: string, keepEol = false): string =
@@ -726,6 +652,10 @@ iterator splitLines*(s: string, keepEol = false): string =
   ##   ""
   ##   "example"
   ##   ""
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
   var first = 0
   var last = 0
   var eolpos = 0
@@ -747,76 +677,102 @@ iterator splitLines*(s: string, keepEol = false): string =
 
     first = last
 
-proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
-  rtl, extern: "nsuSplitLines".} =
-  ## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
-  ## proc that returns a sequence of substrings.
-  accResult(splitLines(s, keepEol=keepEol))
-
-proc countLines*(s: string): int {.noSideEffect,
-  rtl, extern: "nsuCountLines".} =
-  ## Returns the number of lines in the string `s`.
+iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
+  ## Splits the string ``s`` at whitespace stripping leading and trailing
+  ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
+  ## no more than ``maxsplit`` splits is made.
   ##
-  ## This is the same as ``len(splitLines(s))``, but much more efficient
-  ## because it doesn't modify the string creating temporal objects. Every
-  ## `character literal <manual.html#character-literals>`_ newline combination
-  ## (CR, LF, CR-LF) is supported.
+  ## The following code:
   ##
-  ## In this context, a line is any string seperated by a newline combination.
-  ## A line can be an empty string.
-  runnableExamples:
-    doAssert countLines("First line\l and second line.") == 2
-  result = 1
-  var i = 0
-  while i < s.len:
-    case s[i]
-    of '\c':
-      if i+1 < s.len and s[i+1] == '\l': inc i
-      inc result
-    of '\l': inc result
-    else: discard
-    inc i
+  ## .. code-block:: nim
+  ##   let s = "  foo \t bar  baz  "
+  ##   for ms in [-1, 1, 2, 3]:
+  ##     echo "------ maxsplit = ", ms, ":"
+  ##     for item in s.splitWhitespace(maxsplit=ms):
+  ##       echo '"', item, '"'
+  ##
+  ## ...results in:
+  ##
+  ## .. code-block::
+  ##   ------ maxsplit = -1:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##   ------ maxsplit = 1:
+  ##   "foo"
+  ##   "bar  baz  "
+  ##   ------ maxsplit = 2:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz  "
+  ##   ------ maxsplit = 3:
+  ##   "foo"
+  ##   "bar"
+  ##   "baz"
+  ##
+  ## See also:
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  oldSplit(s, Whitespace, maxsplit)
+
 
-proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
-  noSideEffect, rtl, extern: "nsuSplitCharSet".} =
-  ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
-  ## proc that returns a sequence of substrings.
-  runnableExamples:
-    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
-    doAssert "".split({' '}) == @[""]
-  accResult(split(s, seps, maxsplit))
 
 proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitChar".} =
-  ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
-  ## that returns a sequence of substrings.
+  ## The same as the `split iterator <#split.i,string,char,int>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,char,int>`_
+  ## * `rsplit proc<#rsplit,string,char,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a,b,c".split(',') == @["a", "b", "c"]
     doAssert "".split(' ') == @[""]
   accResult(split(s, sep, maxsplit))
 
+proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
+  noSideEffect, rtl, extern: "nsuSplitCharSet".} =
+  ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,set[char],int>`_
+  ## * `rsplit proc<#rsplit,string,set[char],int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  runnableExamples:
+    doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
+    doAssert "".split({' '}) == @[""]
+  accResult(split(s, seps, maxsplit))
+
 proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitString".} =
   ## Splits the string `s` into substrings using a string separator.
   ##
   ## Substrings are separated by the string `sep`. This is a wrapper around the
   ## `split iterator <#split.i,string,string,int>`_.
+  ##
+  ## See also:
+  ## * `split iterator <#split.i,string,string,int>`_
+  ## * `rsplit proc<#rsplit,string,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a,b,c".split(",") == @["a", "b", "c"]
     doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"]
     doAssert "".split("Elon Musk") == @[""]
     doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely", "", "", "", "spaced", "sentence"]
-
     doAssert "a  largely    spaced sentence".split(" ", maxsplit=1) == @["a", " largely    spaced sentence"]
   doAssert(sep.len > 0)
 
   accResult(split(s, sep, maxsplit))
 
-proc rsplit*(s: string, seps: set[char] = Whitespace,
-             maxsplit: int = -1): seq[string]
-             {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
-  ## proc that returns a sequence of substrings.
+proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
+             {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
+  ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -825,20 +781,26 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
   ## do the following to get the tail of the path:
   ##
   ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
   ##
   ## Results in `tailSplit` containing:
   ##
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
-  accResult(rsplit(s, seps, maxsplit))
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,char,int>`_
+  ## * `split proc<#split,string,char,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
-proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
-             {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
-  ## that returns a sequence of substrings.
+proc rsplit*(s: string, seps: set[char] = Whitespace,
+             maxsplit: int = -1): seq[string]
+             {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
+  ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
+  ## proc that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -847,19 +809,24 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
   ## do the following to get the tail of the path:
   ##
   ## .. code-block:: nim
-  ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
+  ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
   ##
   ## Results in `tailSplit` containing:
   ##
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
-  accResult(rsplit(s, sep, maxsplit))
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
+  ## * `split proc<#split,string,set[char],int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  accResult(rsplit(s, seps, maxsplit))
   result.reverse()
 
 proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
              {.noSideEffect, rtl, extern: "nsuRSplitString".} =
-  ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc
+  ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a proc
   ## that returns a sequence of substrings.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
@@ -876,9 +843,13 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   ## .. code-block:: nim
   ##   @["Root#Object#Method", "Index"]
   ##
+  ## See also:
+  ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
+  ## * `split proc<#split,string,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
   runnableExamples:
     doAssert "a  largely    spaced sentence".rsplit(" ", maxsplit=1) == @["a  largely    spaced", "sentence"]
-
     doAssert "a,b,c".rsplit(",") == @["a", "b", "c"]
     doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ", "plan ", "canal panama"]
     doAssert "".rsplit("Elon Musk") == @[""]
@@ -886,6 +857,75 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
   accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
+proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
+  rtl, extern: "nsuSplitLines".} =
+  ## The same as the `splitLines iterator<#splitLines.i,string>`_ (see its
+  ## documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `splitLines iterator<#splitLines.i,string>`_
+  ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
+  ## * `countLines proc<#countLines,string>`_
+  accResult(splitLines(s, keepEol=keepEol))
+
+proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
+  rtl, extern: "nsuSplitWhitespace".} =
+  ## The same as the `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## (see its documentation), but is a proc that returns a sequence of substrings.
+  ##
+  ## See also:
+  ## * `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
+  ## * `splitLines proc<#splitLines,string>`_
+  accResult(splitWhitespace(s, maxsplit))
+
+proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect,
+  rtl, extern: "nsuToBin".} =
+  ## Converts `x` into its binary representation.
+  ##
+  ## The resulting string is always `len` characters long. No leading ``0b``
+  ## prefix is generated.
+  runnableExamples:
+    let
+      a = 29
+      b = 257
+    doAssert a.toBin(8) == "00011101"
+    doAssert b.toBin(8) == "00000001"
+    doAssert b.toBin(9) == "100000001"
+  var
+    mask: BiggestInt = 1
+    shift: BiggestInt = 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((x and mask) shr shift) + ord('0'))
+    shift = shift + 1
+    mask = mask shl 1
+
+proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
+  rtl, extern: "nsuToOct".} =
+  ## Converts `x` into its octal representation.
+  ##
+  ## The resulting string is always `len` characters long. No leading ``0o``
+  ## prefix is generated.
+  ##
+  ## Do not confuse it with `toOctal proc<#toOctal,char>`_.
+  runnableExamples:
+    let
+      a = 62
+      b = 513
+    doAssert a.toOct(3) == "076"
+    doAssert b.toOct(3) == "001"
+    doAssert b.toOct(5) == "01001"
+  var
+    mask: BiggestInt = 7
+    shift: BiggestInt = 0
+  assert(len > 0)
+  result = newString(len)
+  for j in countdown(len-1, 0):
+    result[j] = chr(int((x and mask) shr shift) + ord('0'))
+    shift = shift + 3
+    mask = mask shl 3
+
 proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
   rtl, extern: "nsuToHex".} =
   ## Converts `x` to its hexadecimal representation.
@@ -893,8 +933,12 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
   ## The resulting string will be exactly `len` characters long. No prefix like
   ## ``0x`` is generated. `x` is treated as an unsigned value.
   runnableExamples:
-    doAssert toHex(1984, 6) == "0007C0"
-    doAssert toHex(1984, 2) == "C0"
+    let
+      a = 62
+      b = 4097
+    doAssert a.toHex(3) == "03E"
+    doAssert b.toHex(3) == "001"
+    doAssert b.toHex(4) == "1001"
   const
     HexChars = "0123456789ABCDEF"
   var
@@ -917,6 +961,18 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} =
   ##
   ## The output is twice the input long. No prefix like
   ## ``0x`` is generated.
+  ##
+  ## See also:
+  ## * `parseHexStr proc<#parseHexStr,string>`_ for the reverse operation
+  runnableExamples:
+    let
+      a = "1"
+      b = "A"
+      c = "\0\255"
+    doAssert a.toHex() == "31"
+    doAssert b.toHex() == "41"
+    doAssert c.toHex() == "00FF"
+
   const HexChars = "0123456789ABCDEF"
   result = newString(s.len * 2)
   for pos, c in s:
@@ -925,6 +981,25 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} =
     n = n shr 4
     result[pos * 2] = HexChars[n]
 
+proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
+  ## Converts a character `c` to its octal representation.
+  ##
+  ## The resulting string may not have a leading zero. Its length is always
+  ## exactly 3.
+  ##
+  ## Do not confuse it with `toOct proc<#toOct,BiggestInt,Positive>`_.
+  runnableExamples:
+    doAssert toOctal('1') == "061"
+    doAssert toOctal('A') == "101"
+    doAssert toOctal('a') == "141"
+    doAssert toOctal('!') == "041"
+
+  result = newString(3)
+  var val = ord(c)
+  for i in countdown(2, 0):
+    result[i] = chr(val mod 8 + ord('0'))
+    val = val div 8
+
 proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
   rtl, extern: "nsuIntToStr".} =
   ## Converts `x` to its decimal representation.
@@ -980,9 +1055,10 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar,
 
 proc parseFloat*(s: string): float {.noSideEffect, procvar,
   rtl, extern: "nsuParseFloat".} =
-  ## Parses a decimal floating point value contained in `s`. If `s` is not
-  ## a valid floating point number, `ValueError` is raised. ``NAN``,
-  ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
+  ## Parses a decimal floating point value contained in `s`.
+  ##
+  ## If `s` is not a valid floating point number, `ValueError` is raised.
+  ##``NAN``, ``INF``, ``-INF`` are also supported (case insensitive comparison).
   runnableExamples:
     doAssert parseFloat("3.14") == 3.14
     doAssert parseFloat("inf") == 1.0/0
@@ -997,6 +1073,13 @@ proc parseBinInt*(s: string): int {.noSideEffect, procvar,
   ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
   ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within
   ## `s` are ignored.
+  runnableExamples:
+    let
+      a = "0b11_0101"
+      b = "111"
+    doAssert a.parseBinInt() == 53
+    doAssert b.parseBinInt() == 7
+
   let L = parseutils.parseBin(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid binary integer: " & s)
@@ -1042,11 +1125,20 @@ proc parseHexStr*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nsuParseHexStr".} =
   ## Convert hex-encoded string to byte string, e.g.:
   ##
-  ## .. code-block:: nim
-  ##    hexToStr("00ff") == "\0\255"
-  ##
   ## Raises ``ValueError`` for an invalid hex values. The comparison is
   ## case-insensitive.
+  ##
+  ## See also:
+  ## * `toHex proc<#toHex,string>`_ for the reverse operation
+  runnableExamples:
+    let
+      a = "41"
+      b = "3161"
+      c = "00ff"
+    doAssert parseHexStr(a) == "A"
+    doAssert parseHexStr(b) == "1a"
+    doAssert parseHexStr(c) == "\0\255"
+
   if s.len mod 2 != 0:
     raise newException(ValueError, "Incorrect hex string len")
   result = newString(s.len div 2)
@@ -1067,6 +1159,10 @@ proc parseBool*(s: string): bool =
   ## returns `true`. If ``s`` is one of the following values: ``n, no, false,
   ## 0, off``, then returns `false`.  If ``s`` is something else a
   ## ``ValueError`` exception is raised.
+  runnableExamples:
+    let a = "n"
+    doAssert parseBool(a) == false
+
   case normalize(s)
   of "y", "yes", "true", "1", "on": result = true
   of "n", "no", "false", "0", "off": result = false
@@ -1095,39 +1191,41 @@ proc parseEnum*[T: enum](s: string, default: T): T =
 proc repeat*(c: char, count: Natural): string {.noSideEffect,
   rtl, extern: "nsuRepeatChar".} =
   ## Returns a string of length `count` consisting only of
-  ## the character `c`. You can use this proc to left align strings. Example:
-  ##
-  ## .. code-block:: nim
-  ##   proc tabexpand(indent: int, text: string, tabsize: int = 4) =
-  ##     echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize),
-  ##         text
-  ##
-  ##   tabexpand(4, "At four")
-  ##   tabexpand(5, "At five")
-  ##   tabexpand(6, "At six")
+  ## the character `c`.
+  runnableExamples:
+    let a = 'z'
+    doAssert a.repeat(5) == "zzzzz"
   result = newString(count)
   for i in 0..count-1: result[i] = c
 
 proc repeat*(s: string, n: Natural): string {.noSideEffect,
   rtl, extern: "nsuRepeatStr".} =
-  ## Returns String `s` concatenated `n` times.  Example:
-  ##
-  ## .. code-block:: nim
-  ##   echo "+++ STOP ".repeat(4), "+++"
+  ## Returns string `s` concatenated `n` times.
+  runnableExamples:
+    doAssert "+ foo +".repeat(3) == "+ foo ++ foo ++ foo +"
+
   result = newStringOfCap(n * s.len)
   for i in 1..n: result.add(s)
 
-template spaces*(n: Natural): string = repeat(' ', n)
-  ## Returns a String with `n` space characters. You can use this proc
-  ## to left align strings. Example:
+proc spaces*(n: Natural): string {.inline.} =
+  ## Returns a string with `n` space characters. You can use this proc
+  ## to left align strings.
   ##
-  ## .. code-block:: nim
-  ##   let
-  ##     width = 15
-  ##     text1 = "Hello user!"
-  ##     text2 = "This is a very long string"
-  ##   echo text1 & spaces(max(0, width - text1.len)) & "|"
-  ##   echo text2 & spaces(max(0, width - text2.len)) & "|"
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    let
+      width = 15
+      text1 = "Hello user!"
+      text2 = "This is a very long string"
+    doAssert text1 & spaces(max(0, width - text1.len)) & "|" ==
+             "Hello user!    |"
+    doAssert text2 & spaces(max(0, width - text2.len)) & "|" ==
+             "This is a very long string|"
+  repeat(' ', n)
 
 proc align*(s: string, count: Natural, padding = ' '): string {.
   noSideEffect, rtl, extern: "nsuAlignString".} =
@@ -1136,13 +1234,18 @@ proc align*(s: string, count: Natural, padding = ' '): string {.
   ## `padding` characters (by default spaces) are added before `s` resulting in
   ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
   ## returned unchanged. If you need to left align a string use the `alignLeft
-  ## proc <#alignLeft>`_. Example:
+  ## proc <#alignLeft,string,Natural,Char>`_.
   ##
-  ## .. code-block:: nim
-  ##   assert align("abc", 4) == " abc"
-  ##   assert align("a", 0) == "a"
-  ##   assert align("1232", 6) == "  1232"
-  ##   assert align("1232", 6, '#') == "##1232"
+  ## See also:
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    assert align("abc", 4) == " abc"
+    assert align("a", 0) == "a"
+    assert align("1232", 6) == "  1232"
+    assert align("1232", 6, '#') == "##1232"
   if s.len < count:
     result = newString(count)
     let spaces = count - s.len
@@ -1157,13 +1260,18 @@ proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect
   ## `padding` characters (by default spaces) are added after `s` resulting in
   ## left alignment. If ``s.len >= count``, no spaces are added and `s` is
   ## returned unchanged. If you need to right align a string use the `align
-  ## proc <#align>`_. Example:
+  ## proc <#align,string,Natural,Char>`_.
   ##
-  ## .. code-block:: nim
-  ##   assert alignLeft("abc", 4) == "abc "
-  ##   assert alignLeft("a", 0) == "a"
-  ##   assert alignLeft("1232", 6) == "1232  "
-  ##   assert alignLeft("1232", 6, '#') == "1232##"
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  ## * `center proc<#center,string,int,char>`_
+  runnableExamples:
+    assert alignLeft("abc", 4) == "abc "
+    assert alignLeft("a", 0) == "a"
+    assert alignLeft("1232", 6) == "1232  "
+    assert alignLeft("1232", 6, '#') == "1232##"
   if s.len < count:
     result = newString(count)
     if s.len > 0:
@@ -1173,83 +1281,55 @@ proc alignLeft*(s: string, count: Natural, padding = ' '): string {.noSideEffect
   else:
     result = s
 
-iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
-  token: string, isSep: bool] =
-  ## Tokenizes the string `s` into substrings.
-  ##
-  ## Substrings are separated by a substring containing only `seps`.
-  ## Examples:
-  ##
-  ## .. code-block:: nim
-  ##   for word in tokenize("  this is an  example  "):
-  ##     writeLine(stdout, word)
+proc center*(s: string, width: int, fillChar: char = ' '): string {.
+  noSideEffect, rtl, extern: "nsuCenterString".} =
+  ## Return the contents of `s` centered in a string `width` long using
+  ## `fillChar` (default: space) as padding.
   ##
-  ## Results in:
+  ## The original string is returned if `width` is less than or equal
+  ## to `s.len`.
   ##
-  ## .. code-block:: nim
-  ##   ("  ", true)
-  ##   ("this", false)
-  ##   (" ", true)
-  ##   ("is", false)
-  ##   (" ", true)
-  ##   ("an", false)
-  ##   ("  ", true)
-  ##   ("example", false)
-  ##   ("  ", true)
-  var i = 0
-  while true:
-    var j = i
-    var isSep = j < s.len and s[j] in seps
-    while j < s.len and (s[j] in seps) == isSep: inc(j)
-    if j > i:
-      yield (substr(s, i, j-1), isSep)
-    else:
-      break
-    i = j
-
-proc wordWrap*(s: string, maxLineWidth = 80,
-               splitLongWords = true,
-               seps: set[char] = Whitespace,
-               newLine = "\n"): string {.
-               noSideEffect, rtl, extern: "nsuWordWrap",
-               deprecated: "use wrapWords in std/wordwrap instead".} =
-  ## Word wraps `s`.
-  result = newStringOfCap(s.len + s.len shr 6)
-  var spaceLeft = maxLineWidth
-  var lastSep = ""
-  for word, isSep in tokenize(s, seps):
-    if isSep:
-      lastSep = word
-      spaceLeft = spaceLeft - len(word)
-      continue
-    if len(word) > spaceLeft:
-      if splitLongWords and len(word) > maxLineWidth:
-        result.add(substr(word, 0, spaceLeft-1))
-        var w = spaceLeft
-        var wordLeft = len(word) - spaceLeft
-        while wordLeft > 0:
-          result.add(newLine)
-          var L = min(maxLineWidth, wordLeft)
-          spaceLeft = maxLineWidth - L
-          result.add(substr(word, w, w+L-1))
-          inc(w, L)
-          dec(wordLeft, L)
-      else:
-        spaceLeft = maxLineWidth - len(word)
-        result.add(newLine)
-        result.add(word)
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  runnableExamples:
+    let a = "foo"
+    doAssert a.center(2) == "foo"
+    doAssert a.center(5) == " foo "
+    doAssert a.center(6) == " foo  "
+  if width <= s.len: return s
+  result = newString(width)
+  # Left padding will be one fillChar
+  # smaller if there are an odd number
+  # of characters
+  let
+    charsLeft = (width - s.len)
+    leftPadding = charsLeft div 2
+  for i in 0 ..< width:
+    if i >= leftPadding and i < leftPadding + s.len:
+      # we are where the string should be located
+      result[i] = s[i-leftPadding]
     else:
-      spaceLeft = spaceLeft - len(word)
-      result.add(lastSep & word)
-      lastSep.setLen(0)
+      # we are either before or after where
+      # the string s should go
+      result[i] = fillChar
 
 proc indent*(s: string, count: Natural, padding: string = " "): string
     {.noSideEffect, rtl, extern: "nsuIndent".} =
   ## Indents each line in ``s`` by ``count`` amount of ``padding``.
   ##
   ## **Note:** This does not preserve the new line characters used in ``s``.
+  ##
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `unindent proc<#unindent,string,Natural,string>`_
   runnableExamples:
-    doAssert indent("First line\c\l and second line.", 2) == "  First line\l   and second line."
+    doAssert indent("First line\c\l and second line.", 2) ==
+             "  First line\l   and second line."
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1266,8 +1346,15 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
   ## Sometimes called `dedent`:idx:
   ##
   ## **Note:** This does not preserve the new line characters used in ``s``.
+  ##
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
   runnableExamples:
-    doAssert unindent("  First line\l   and second line", 3) == "First line\land second line"
+    doAssert unindent("  First line\l   and second line", 3) ==
+             "First line\land second line"
   result = ""
   var i = 0
   for line in s.splitLines():
@@ -1286,37 +1373,105 @@ proc unindent*(s: string): string
     {.noSideEffect, rtl, extern: "nsuUnindentAll".} =
   ## Removes all indentation composed of whitespace from each line in ``s``.
   ##
-  ## For example:
+  ## See also:
+  ## * `align proc<#align,string,Natural,Char>`_
+  ## * `alignLeft proc<#alignLeft,string,Natural,Char>`_
+  ## * `spaces proc<#spaces,Natural>`_
+  ## * `indent proc<#indent,string,Natural,string>`_
+  runnableExamples:
+    let x = """
+      Hello
+      There
+    """.unindent()
+
+    doAssert x == "Hello\nThere\n"
+  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+
+proc delete*(s: var string, first, last: int) {.noSideEffect,
+  rtl, extern: "nsuDelete".} =
+  ## Deletes in `s` (must be declared as ``var``) the characters at positions
+  ## ``first ..last`` (both ends included).
   ##
-  ## .. code-block:: nim
-  ##   const x = """
-  ##     Hello
-  ##     There
-  ##   """.unindent()
+  ## This modifies `s` itself, it does not return a copy.
+  runnableExamples:
+    var a = "abracadabra"
+
+    a.delete(4, 5)
+    doAssert a == "abradabra"
+
+    a.delete(1, 6)
+    doAssert a == "ara"
+
+  var i = first
+  var j = last+1
+  var newLen = len(s)-j+i
+  while i < newLen:
+    s[i] = s[j]
+    inc(i)
+    inc(j)
+  setLen(s, newLen)
+
+
+proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` starts with character ``prefix``.
   ##
-  ##   doAssert x == "Hello\nThere\n"
-  unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish.
+  ## See also:
+  ## * `endsWith proc<#endsWith,string,char>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix proc<#removePrefix,string,char>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.startsWith('a') == true
+    doAssert a.startsWith('b') == false
+  result = s.len > 0 and s[0] == prefix
 
 proc startsWith*(s, prefix: string): bool {.noSideEffect,
   rtl, extern: "nsuStartsWith".} =
-  ## Returns true iff ``s`` starts with ``prefix``.
+  ## Returns true if ``s`` starts with string ``prefix``.
   ##
   ## If ``prefix == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `endsWith proc<#endsWith,string,string>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removePrefix proc<#removePrefix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.startsWith("abra") == true
+    doAssert a.startsWith("bra") == false
   var i = 0
   while true:
     if i >= prefix.len: return true
     if i >= s.len or s[i] != prefix[i]: return false
     inc(i)
 
-proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
-  ## Returns true iff ``s`` starts with ``prefix``.
-  result = s.len > 0 and s[0] == prefix
+proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` ends with ``suffix``.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,char>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix proc<#removeSuffix,string,char>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.endsWith('a') == true
+    doAssert a.endsWith('b') == false
+  result = s.len > 0 and s[s.high] == suffix
 
 proc endsWith*(s, suffix: string): bool {.noSideEffect,
   rtl, extern: "nsuEndsWith".} =
-  ## Returns true iff ``s`` ends with ``suffix``.
+  ## Returns true if ``s`` ends with ``suffix``.
   ##
   ## If ``suffix == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,string>`_
+  ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
+  ## * `removeSuffix proc<#removeSuffix,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.endsWith("abra") == true
+    doAssert a.endsWith("dab") == false
   var i = 0
   var j = len(s) - len(suffix)
   while i+j <% s.len:
@@ -1324,21 +1479,136 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
     inc(i)
   if i >= suffix.len: return true
 
-proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
-  ## Returns true iff ``s`` ends with ``suffix``.
-  result = s.len > 0 and s[s.high] == suffix
-
 proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
   rtl, extern: "nsuContinuesWith".} =
-  ## Returns true iff ``s`` continues with ``substr`` at position ``start``.
+  ## Returns true if ``s`` continues with ``substr`` at position ``start``.
   ##
   ## If ``substr == ""`` true is returned.
+  ##
+  ## See also:
+  ## * `startsWith proc<#startsWith,string,string>`_
+  ## * `endsWith proc<#endsWith,string,string>`_
+  runnableExamples:
+    let a = "abracadabra"
+    doAssert a.continuesWith("ca", 4) == true
+    doAssert a.continuesWith("ca", 5) == false
+    doAssert a.continuesWith("dab", 6) == true
   var i = 0
   while true:
     if i >= substr.len: return true
     if i+start >= s.len or s[i+start] != substr[i]: return false
     inc(i)
 
+
+proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
+  rtl, extern: "nsuRemovePrefixCharSet".} =
+  ## Removes all characters from `chars` from the start of the string `s`
+  ## (in-place).
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,set[char]>`_
+  runnableExamples:
+     var userInput = "\r\n*~Hello World!"
+     userInput.removePrefix
+     doAssert userInput == "*~Hello World!"
+     userInput.removePrefix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "?!?Hello!?!"
+     otherInput.removePrefix({'!', '?'})
+     doAssert otherInput == "Hello!?!"
+
+  var start = 0
+  while start < s.len and s[start] in chars: start += 1
+  if start > 0: s.delete(0, start - 1)
+
+proc removePrefix*(s: var string, c: char) {.
+  rtl, extern: "nsuRemovePrefixChar".} =
+  ## Removes all occurrences of a single character (in-place) from the start
+  ## of a string.
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,char>`_
+  ## * `startsWith proc<#startsWith,string,char>`_
+  runnableExamples:
+    var ident = "pControl"
+    ident.removePrefix('p')
+    doAssert ident == "Control"
+  removePrefix(s, chars = {c})
+
+proc removePrefix*(s: var string, prefix: string) {.
+  rtl, extern: "nsuRemovePrefixString".} =
+  ## Remove the first matching prefix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removeSuffix proc<#removeSuffix,string,string>`_
+  ## * `startsWith proc<#startsWith,string,string>`_
+  runnableExamples:
+     var answers = "yesyes"
+     answers.removePrefix("yes")
+     doAssert answers == "yes"
+  if s.startsWith(prefix):
+    s.delete(0, prefix.len - 1)
+
+proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
+  rtl, extern: "nsuRemoveSuffixCharSet".} =
+  ## Removes all characters from `chars` from the end of the string `s`
+  ## (in-place).
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,set[char]>`_
+  runnableExamples:
+     var userInput = "Hello World!*~\r\n"
+     userInput.removeSuffix
+     doAssert userInput == "Hello World!*~"
+     userInput.removeSuffix({'~', '*'})
+     doAssert userInput == "Hello World!"
+
+     var otherInput = "Hello!?!"
+     otherInput.removeSuffix({'!', '?'})
+     doAssert otherInput == "Hello"
+
+  if s.len == 0: return
+  var last = s.high
+  while last > -1 and s[last] in chars: last -= 1
+  s.setLen(last + 1)
+
+proc removeSuffix*(s: var string, c: char) {.
+  rtl, extern: "nsuRemoveSuffixChar".} =
+  ## Removes all occurrences of a single character (in-place) from the end
+  ## of a string.
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,char>`_
+  ## * `endsWith proc<#endsWith,string,char>`_
+  runnableExamples:
+    var table = "users"
+    table.removeSuffix('s')
+    doAssert table == "user"
+
+    var dots = "Trailing dots......."
+    dots.removeSuffix('.')
+    doAssert dots == "Trailing dots"
+
+  removeSuffix(s, chars = {c})
+
+proc removeSuffix*(s: var string, suffix: string) {.
+  rtl, extern: "nsuRemoveSuffixString".} =
+  ## Remove the first matching suffix (in-place) from a string.
+  ##
+  ## See also:
+  ## * `removePrefix proc<#removePrefix,string,string>`_
+  ## * `endsWith proc<#endsWith,string,string>`_
+  runnableExamples:
+    var answers = "yeses"
+    answers.removeSuffix("es")
+    doAssert answers == "yes"
+  var newLen = s.len
+  if s.endsWith(suffix):
+    newLen -= len(suffix)
+    s.setLen(newLen)
+
+
 proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
   {.noSideEffect, inline.} =
   ## Adds a separator to `dest` only if its length is bigger than `startLen`.
@@ -1353,24 +1623,28 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
   ## `startLen`. The following example creates a string describing
   ## an array of integers.
   runnableExamples:
-     var arr = "["
-     for x in items([2, 3, 5, 7, 11]):
-       addSep(arr, startLen=len("["))
-       add(arr, $x)
-     add(arr, "]")
+    var arr = "["
+    for x in items([2, 3, 5, 7, 11]):
+      addSep(arr, startLen=len("["))
+      add(arr, $x)
+    add(arr, "]")
+    doAssert arr == "[2, 3, 5, 7, 11]"
+
   if dest.len > startLen: add(dest, sep)
 
 proc allCharsInSet*(s: string, theSet: set[char]): bool =
-  ## Returns true iff each character of `s` is in the set `theSet`.
+  ## Returns true if every character of `s` is in the set `theSet`.
   runnableExamples:
     doAssert allCharsInSet("aeea", {'a', 'e'}) == true
     doAssert allCharsInSet("", {'a', 'e'}) == true
+
   for c in items(s):
     if c notin theSet: return false
   return true
 
 proc abbrev*(s: string, possibilities: openArray[string]): int =
-  ## Returns the index of the first item in ``possibilities`` which starts with ``s``, if not ambiguous.
+  ## Returns the index of the first item in ``possibilities`` which starts
+  ## with ``s``, if not ambiguous.
   ##
   ## Returns -1 if no item has been found and -2 if multiple items match.
   runnableExamples:
@@ -1378,6 +1652,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
     doAssert abbrev("foo", ["college", "faculty", "industry"]) == -1 # Not found
     doAssert abbrev("fac", ["college", "faculty", "faculties"]) == -2 # Ambiguous
     doAssert abbrev("college", ["college", "colleges", "industry"]) == 0
+
   result = -1 # none found
   for i in 0..possibilities.len-1:
     if possibilities[i].startsWith(s):
@@ -1391,9 +1666,10 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
 
 proc join*(a: openArray[string], sep: string = ""): string {.
   noSideEffect, rtl, extern: "nsuJoinSep".} =
-  ## Concatenates all strings in `a` separating them with `sep`.
+  ## Concatenates all strings in the container `a`, separating them with `sep`.
   runnableExamples:
     doAssert join(["A", "B", "Conclusion"], " -> ") == "A -> B -> Conclusion"
+
   if len(a) > 0:
     var L = sep.len * (a.len-1)
     for i in 0..high(a): inc(L, a[i].len)
@@ -1407,10 +1683,11 @@ proc join*(a: openArray[string], sep: string = ""): string {.
 
 proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
   noSideEffect, rtl.} =
-  ## Converts all elements in `a` to strings using `$` and concatenates them
-  ## with `sep`.
+  ## Converts all elements in the container `a` to strings using `$`,
+  ## and concatenates them with `sep`.
   runnableExamples:
     doAssert join([1, 2, 3], " -> ") == "1 -> 2 -> 3"
+
   result = ""
   for i, x in a:
     if i > 0:
@@ -1441,14 +1718,13 @@ proc initSkipTable*(a: var SkipTable, sub: string)
 
 proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
   {.noSideEffect, rtl, extern: "nsuFindStrA".} =
-  ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed table `a`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed
+  ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last
+  ## element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-
   let
     last = if last==0: s.high else: last
-    sLen = last - start + 1
     subLast = sub.len - 1
 
   if subLast == -1:
@@ -1467,7 +1743,6 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
         return skip
       dec i
     inc skip, a[s[skip + subLast]]
-
   return -1
 
 when not (defined(js) or defined(nimdoc) or defined(nimscript)):
@@ -1479,10 +1754,14 @@ else:
 
 proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffect,
   rtl, extern: "nsuFindChar".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,char,int>`_
+  ## * `replace proc<#replace,string,char,char>`_
   let last = if last==0: s.high else: last
   when nimvm:
     for i in int(start)..last:
@@ -1499,34 +1778,72 @@ proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffe
         if sub == s[i]: return i
   return -1
 
+proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect,
+  rtl, extern: "nsuFindCharSet".} =
+  ## Searches for `chars` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
+  ##
+  ## If `s` contains none of the characters in `chars`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,set[char],int>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
+  let last = if last==0: s.high else: last
+  for i in int(start)..last:
+    if s[i] in chars: return i
+  return -1
+
 proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect,
   rtl, extern: "nsuFindStr".} =
-  ## Searches for `sub` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+  ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `rfind proc<#rfind,string,string,int>`_
+  ## * `replace proc<#replace,string,string,string>`_
   if sub.len > s.len: return -1
   if sub.len == 1: return find(s, sub[0], start, last)
   var a {.noinit.}: SkipTable
   initSkipTable(a, sub)
   result = find(a, s, sub, start, last)
 
-proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect,
-  rtl, extern: "nsuFindCharSet".} =
-  ## Searches for `chars` in `s` inside range `start`..`last`.
-  ## If `last` is unspecified, it defaults to `s.high`.
+proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect,
+  rtl.} =
+  ## Searches for characer `sub` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
   ##
-  ## If `s` contains none of the characters in `chars`, -1 is returned.
-  let last = if last==0: s.high else: last
-  for i in int(start)..last:
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,char,int,int>`_
+  let realStart = if start == -1: s.len-1 else: start
+  for i in countdown(realStart, 0):
+    if sub == s[i]: return i
+  return -1
+
+proc rfind*(s: string, chars: set[char], start: int = -1): int {.noSideEffect.} =
+  ## Searches for `chars` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,set[char],Natural,int>`_
+  let realStart = if start == -1: s.len-1 else: start
+  for i in countdown(realStart, 0):
     if s[i] in chars: return i
   return -1
 
 proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
-  ## Searches for `sub` in `s` in reverse, starting at `start` and going
-  ## backwards to 0.
+  ## Searches for string `sub` in `s` in reverse, starting at position `start`
+  ## (default: the last character) and going backwards to the first character.
   ##
   ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
   if sub.len == 0:
     return -1
   let realStart = if start == -1: s.len else: start
@@ -1539,54 +1856,34 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
     if result != -1: return
   return -1
 
-proc rfind*(s: string, sub: char, start: int = -1): int {.noSideEffect,
-  rtl.} =
-  ## Searches for `sub` in `s` in reverse starting at position `start`.
-  ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  let realStart = if start == -1: s.len-1 else: start
-  for i in countdown(realStart, 0):
-    if sub == s[i]: return i
-  return -1
 
-proc rfind*(s: string, chars: set[char], start: int = -1): int {.noSideEffect.} =
-  ## Searches for `chars` in `s` in reverse starting at position `start`.
+proc count*(s: string, sub: char): int {.noSideEffect,
+  rtl, extern: "nsuCountChar".} =
+  ## Count the occurrences of the character `sub` in the string `s`.
   ##
-  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
-  let realStart = if start == -1: s.len-1 else: start
-  for i in countdown(realStart, 0):
-    if s[i] in chars: return i
-  return -1
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
+  for c in s:
+    if c == sub: inc result
 
-proc center*(s: string, width: int, fillChar: char = ' '): string {.
-  noSideEffect, rtl, extern: "nsuCenterString".} =
-  ## Return the contents of `s` centered in a string `width` long using
-  ## `fillChar` as padding.
+proc count*(s: string, subs: set[char]): int {.noSideEffect,
+  rtl, extern: "nsuCountCharSet".} =
+  ## Count the occurrences of the group of character `subs` in the string `s`.
   ##
-  ## The original string is returned if `width` is less than or equal
-  ## to `s.len`.
-  if width <= s.len: return s
-  result = newString(width)
-  # Left padding will be one fillChar
-  # smaller if there are an odd number
-  # of characters
-  let
-    charsLeft = (width - s.len)
-    leftPadding = charsLeft div 2
-  for i in 0 ..< width:
-    if i >= leftPadding and i < leftPadding + s.len:
-      # we are where the string should be located
-      result[i] = s[i-leftPadding]
-    else:
-      # we are either before or after where
-      # the string s should go
-      result[i] = fillChar
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
+  doAssert card(subs) > 0
+  for c in s:
+    if c in subs: inc result
 
 proc count*(s: string, sub: string, overlapping: bool = false): int {.
   noSideEffect, rtl, extern: "nsuCountString".} =
   ## Count the occurrences of a substring `sub` in the string `s`.
   ## Overlapping occurrences of `sub` only count when `overlapping`
-  ## is set to true.
+  ## is set to true (default: false).
+  ##
+  ## See also:
+  ## * `countLines proc<#countLines,string>`_
   doAssert sub.len > 0
   var i = 0
   while true:
@@ -1596,43 +1893,58 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
     else: i += sub.len
     inc result
 
-proc count*(s: string, sub: char): int {.noSideEffect,
-  rtl, extern: "nsuCountChar".} =
-  ## Count the occurrences of the character `sub` in the string `s`.
-  for c in s:
-    if c == sub: inc result
-
-proc count*(s: string, subs: set[char]): int {.noSideEffect,
-  rtl, extern: "nsuCountCharSet".} =
-  ## Count the occurrences of the group of character `subs` in the string `s`.
-  doAssert card(subs) > 0
-  for c in s:
-    if c in subs: inc result
-
-proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
-  ## Returns ``'"' & s & '"'`` if `s` contains a space and does not
-  ## start with a quote, else returns `s`.
+proc countLines*(s: string): int {.noSideEffect,
+  rtl, extern: "nsuCountLines".} =
+  ## Returns the number of lines in the string `s`.
+  ##
+  ## This is the same as ``len(splitLines(s))``, but much more efficient
+  ## because it doesn't modify the string creating temporal objects. Every
+  ## `character literal <manual.html#lexical-analysis-character-literals>`_
+  ## newline combination (CR, LF, CR-LF) is supported.
   ##
-  ## **DEPRECATED** as it was confused for shell quoting function.  For this
-  ## application use `osproc.quoteShell <osproc.html#quoteShell>`_.
-  if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"'
-  else: result = s
+  ## In this context, a line is any string seperated by a newline combination.
+  ## A line can be an empty string.
+  ##
+  ## See also:
+  ## * `splitLines proc<#splitLines,string>`_
+  runnableExamples:
+    doAssert countLines("First line\l and second line.") == 2
+  result = 1
+  var i = 0
+  while i < s.len:
+    case s[i]
+    of '\c':
+      if i+1 < s.len and s[i+1] == '\l': inc i
+      inc result
+    of '\l': inc result
+    else: discard
+    inc i
 
-proc contains*(s: string, c: char): bool {.noSideEffect.} =
-  ## Same as ``find(s, c) >= 0``.
-  return find(s, c) >= 0
 
 proc contains*(s, sub: string): bool {.noSideEffect.} =
   ## Same as ``find(s, sub) >= 0``.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
   return find(s, sub) >= 0
 
 proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
   ## Same as ``find(s, chars) >= 0``.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,set[char],Natural,int>`_
   return find(s, chars) >= 0
 
 proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   rtl, extern: "nsuReplaceStr".} =
   ## Replaces `sub` in `s` by the string `by`.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,string,Natural,int>`_
+  ## * `replace proc<#replace,string,char,char>`_ for replacing
+  ##   single characters
+  ## * `replaceWord proc<#replaceWord,string,string,string>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
   result = ""
   let subLen = sub.len
   if subLen == 0:
@@ -1670,6 +1982,11 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect,
   ## Replaces `sub` in `s` by the character `by`.
   ##
   ## Optimized version of `replace <#replace,string,string>`_ for characters.
+  ##
+  ## See also:
+  ## * `find proc<#find,string,char,Natural,int>`_
+  ## * `replaceWord proc<#replaceWord,string,string,string>`_
+  ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
   result = newString(s.len)
   var i = 0
   while i < s.len:
@@ -1682,7 +1999,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   ## Replaces `sub` in `s` by the string `by`.
   ##
   ## Each occurrence of `sub` has to be surrounded by word boundaries
-  ## (comparable to ``\\w`` in regular expressions), otherwise it is not
+  ## (comparable to ``\b`` in regular expressions), otherwise it is not
   ## replaced.
   if sub.len == 0: return s
   const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
@@ -1712,14 +2029,14 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
   ## Same as replace, but specialized for doing multiple replacements in a single
   ## pass through the input string.
   ##
-  ## multiReplace performs all replacements in a single pass, this means it can be used
-  ## to swap the occurences of "a" and "b", for instance.
+  ## `multiReplace` performs all replacements in a single pass, this means it
+  ## can be used to swap the occurences of "a" and "b", for instance.
   ##
-  ## If the resulting string is not longer than the original input string, only a single
-  ## memory allocation is required.
+  ## If the resulting string is not longer than the original input string,
+  ## only a single memory allocation is required.
   ##
-  ## The order of the replacements does matter. Earlier replacements are preferred over later
-  ## replacements in the argument list.
+  ## The order of the replacements does matter. Earlier replacements are
+  ## preferred over later replacements in the argument list.
   result = newStringOfCap(s.len)
   var i = 0
   var fastChk: set[char] = {}
@@ -1741,55 +2058,12 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {
       add result, s[i]
       inc(i)
 
-proc delete*(s: var string, first, last: int) {.noSideEffect,
-  rtl, extern: "nsuDelete".} =
-  ## Deletes in `s` the characters at position `first` .. `last`.
-  ##
-  ## This modifies `s` itself, it does not return a copy.
-  var i = first
-  var j = last+1
-  var newLen = len(s)-j+i
-  while i < newLen:
-    s[i] = s[j]
-    inc(i)
-    inc(j)
-  setLen(s, newLen)
 
-proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
-  rtl, extern: "nsuToOct".} =
-  ## Converts `x` into its octal representation.
-  ##
-  ## The resulting string is always `len` characters long. No leading ``0o``
-  ## prefix is generated.
-  var
-    mask: BiggestInt = 7
-    shift: BiggestInt = 0
-  assert(len > 0)
-  result = newString(len)
-  for j in countdown(len-1, 0):
-    result[j] = chr(int((x and mask) shr shift) + ord('0'))
-    shift = shift + 3
-    mask = mask shl 3
-
-proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect,
-  rtl, extern: "nsuToBin".} =
-  ## Converts `x` into its binary representation.
-  ##
-  ## The resulting string is always `len` characters long. No leading ``0b``
-  ## prefix is generated.
-  var
-    mask: BiggestInt = 1
-    shift: BiggestInt = 0
-  assert(len > 0)
-  result = newString(len)
-  for j in countdown(len-1, 0):
-    result[j] = chr(int((x and mask) shr shift) + ord('0'))
-    shift = shift + 1
-    mask = mask shl 1
 
 proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
   rtl, extern: "nsuInsertSep".} =
-  ## Inserts the separator `sep` after `digits` digits from right to left.
+  ## Inserts the separator `sep` after `digits` characters (default: 3)
+  ## from right to left.
   ##
   ## Even though the algorithm works with any string `s`, it is only useful
   ## if `s` contains a number.
@@ -1811,11 +2085,15 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
 
 proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   rtl, extern: "nsuEscape".} =
-  ## Escapes a string `s`. See `system.addEscapedChar <system.html#addEscapedChar>`_
-  ## for the escaping scheme.
+  ## Escapes a string `s`. See `system.addEscapedChar
+  ## <system.html#addEscapedChar,string,char>`_ for the escaping scheme.
   ##
   ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
   ## Both may be empty strings.
+  ##
+  ## See also:
+  ## * `unescape proc<#unescape,string,string,string>`_ for the opposite
+  ## operation
   result = newStringOfCap(s.len + s.len shr 2)
   result.add(prefix)
   for c in items(s):
@@ -1833,8 +2111,8 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
   rtl, extern: "nsuUnescape".} =
   ## Unescapes a string `s`.
   ##
-  ## This complements `escape <#escape>`_ as it performs the opposite
-  ## operations.
+  ## This complements `escape proc<#escape,string,string,string>`_
+  ## as it performs the opposite operations.
   ##
   ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
   ## ValueError exception will be raised.
@@ -1880,101 +2158,12 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
   ## and is followed by any number of characters of the set `IdentChars`.
   runnableExamples:
     doAssert "abc_def08".validIdentifier
+
   if s.len > 0 and s[0] in IdentStartChars:
     for i in 1..s.len-1:
       if s[i] notin IdentChars: return false
     return true
 
-{.push warning[Deprecated]: off.}
-proc editDistance*(a, b: string): int {.noSideEffect,
-  rtl, extern: "nsuEditDistance",
-  deprecated: "use editdistance.editDistanceAscii instead".} =
-  ## Returns the edit distance between `a` and `b`.
-  ##
-  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
-  ## memory overhead.
-  var len1 = a.len
-  var len2 = b.len
-  if len1 > len2:
-    # make `b` the longer string
-    return editDistance(b, a)
-
-  # strip common prefix:
-  var s = 0
-  while s < len1 and a[s] == b[s]:
-    inc(s)
-    dec(len1)
-    dec(len2)
-  # strip common suffix:
-  while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
-    dec(len1)
-    dec(len2)
-  # trivial cases:
-  if len1 == 0: return len2
-  if len2 == 0: return len1
-
-  # another special case:
-  if len1 == 1:
-    for j in s..s+len2-1:
-      if a[s] == b[j]: return len2 - 1
-    return len2
-
-  inc(len1)
-  inc(len2)
-  var half = len1 shr 1
-  # initalize first row:
-  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
-  var row: seq[int]
-  newSeq(row, len2)
-  var e = s + len2 - 1 # end marker
-  for i in 1..len2 - half - 1: row[i] = i
-  row[0] = len1 - half - 1
-  for i in 1 .. len1 - 1:
-    var char1 = a[i + s - 1]
-    var char2p: int
-    var D, x: int
-    var p: int
-    if i >= len1 - half:
-      # skip the upper triangle:
-      var offset = i - len1 + half
-      char2p = offset
-      p = offset
-      var c3 = row[p] + ord(char1 != b[s + char2p])
-      inc(p)
-      inc(char2p)
-      x = row[p] + 1
-      D = x
-      if x > c3: x = c3
-      row[p] = x
-      inc(p)
-    else:
-      p = 1
-      char2p = 0
-      D = i
-      x = i
-    if i <= half + 1:
-      # skip the lower triangle:
-      e = len2 + i - half - 2
-    # main:
-    while p <= e:
-      dec(D)
-      var c3 = D + ord(char1 != b[char2p + s])
-      inc(char2p)
-      inc(x)
-      if x > c3: x = c3
-      D = row[p] + 1
-      if x > D: x = D
-      row[p] = x
-      inc(p)
-    # lower triangle sentinel:
-    if i <= half:
-      dec(D)
-      var c3 = D + ord(char1 != b[char2p + s])
-      inc(x)
-      if x > c3: x = c3
-      row[p] = x
-  result = row[e]
-{.pop.}
 
 # floating point formating:
 when not defined(js):
@@ -1982,10 +2171,11 @@ when not defined(js):
                                      importc: "sprintf", varargs, noSideEffect.}
 
 type
-  FloatFormatMode* = enum ## the different modes of floating point formating
-    ffDefault,         ## use the shorter floating point notation
-    ffDecimal,         ## use decimal floating point notation
-    ffScientific       ## use scientific notation (using ``e`` character)
+  FloatFormatMode* = enum
+    ## the different modes of floating point formating
+    ffDefault,   ## use the shorter floating point notation
+    ffDecimal,   ## use decimal floating point notation
+    ffScientific ## use scientific notation (using ``e`` character)
 
 proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
                          precision: range[-1..32] = 16;
@@ -2001,6 +2191,11 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
   ## after the decimal point for Nim's ``biggestFloat`` type.
   ##
   ## If ``precision == -1``, it tries to format it nicely.
+  runnableExamples:
+    let x = 123.456
+    doAssert x.formatBiggestFloat() == "123.4560000000000"
+    doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560"
+    doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02"
   when defined(js):
     var precision = precision
     if precision == -1:
@@ -2080,11 +2275,18 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
     doAssert x.formatFloat() == "123.4560000000000"
     doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
     doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
+
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
 proc trimZeros*(x: var string) {.noSideEffect.} =
   ## Trim trailing zeros from a formatted floating point
-  ## value (`x`).  Modifies the passed value.
+  ## value `x` (must be declared as ``var``).
+  ##
+  ## This modifies `x` itself, it does not return a copy.
+  runnableExamples:
+    var x = "123.456000000"
+    x.trimZeros()
+    doAssert x == "123.456"
   var spl: seq[string]
   if x.contains('.') or x.contains(','):
     if x.contains('e'):
@@ -2114,6 +2316,9 @@ proc formatSize*(bytes: int64,
   ##
   ## `includeSpace` can be set to true to include the (SI preferred) space
   ## between the number and the unit (e.g. 1 KiB).
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   runnableExamples:
     doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
     doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
@@ -2121,6 +2326,7 @@ proc formatSize*(bytes: int64,
     doAssert formatSize(4096, prefix=bpColloquial, includeSpace=true) == "4 kB"
     doAssert formatSize(4096) == "4KiB"
     doAssert formatSize(5_378_934, prefix=bpColloquial, decimalSep=',') == "5,13MB"
+
   const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
   const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
   var
@@ -2216,6 +2422,9 @@ proc formatEng*(f: BiggestFloat,
   ##    formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " # Space with useUnitSpace=true
   ##
   ## `decimalSep` is used as the decimal separator.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   var
     absolute: BiggestFloat
     significand: BiggestFloat
@@ -2403,110 +2612,69 @@ proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
   ##
   ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
   ## raised if an ill-formed format string has been passed to the `%` operator.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   result = newStringOfCap(formatstr.len + a.len shl 4)
   addf(result, formatstr, a)
 
 proc `%` *(formatstr, a: string): string {.noSideEffect,
   rtl, extern: "nsuFormatSingleElem".} =
-  ## This is the same as ``formatstr % [a]``.
+  ## This is the same as ``formatstr % [a]`` (see
+  ## `% proc<#%25,string,openArray[string]>`_).
   result = newStringOfCap(formatstr.len + a.len)
   addf(result, formatstr, [a])
 
 proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect,
   rtl, extern: "nsuFormatVarargs".} =
-  ## This is the same as ``formatstr % a`` except that it supports
+  ## This is the same as ``formatstr % a`` (see
+  ## `% proc<#%25,string,openArray[string]>`_) except that it supports
   ## auto stringification.
+  ##
+  ## See also:
+  ## * `strformat module<strformat.html>`_ for string interpolation and formatting
   result = newStringOfCap(formatstr.len + a.len)
   addf(result, formatstr, a)
 
 {.pop.}
 
-proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
-  rtl, extern: "nsuRemoveSuffixCharSet".} =
-  ## Removes all characters from `chars` from the end of the string `s`
-  ## (in-place).
-  runnableExamples:
-     var userInput = "Hello World!*~\r\n"
-     userInput.removeSuffix
-     doAssert userInput == "Hello World!*~"
-     userInput.removeSuffix({'~', '*'})
-     doAssert userInput == "Hello World!"
 
-     var otherInput = "Hello!?!"
-     otherInput.removeSuffix({'!', '?'})
-     doAssert otherInput == "Hello"
-  if s.len == 0: return
-  var last = s.high
-  while last > -1 and s[last] in chars: last -= 1
-  s.setLen(last + 1)
 
-proc removeSuffix*(s: var string, c: char) {.
-  rtl, extern: "nsuRemoveSuffixChar".} =
-  ## Removes all occurrences of a single character (in-place) from the end
-  ## of a string.
+proc strip*(s: string, leading = true, trailing = true,
+            chars: set[char] = Whitespace): string
+  {.noSideEffect, rtl, extern: "nsuStrip".} =
+  ## Strips leading or trailing `chars` (default: whitespace characters)
+  ## from `s` and returns the resulting string.
   ##
-  runnableExamples:
-     var table = "users"
-     table.removeSuffix('s')
-     doAssert table == "user"
-
-     var dots = "Trailing dots......."
-     dots.removeSuffix('.')
-     doAssert dots == "Trailing dots"
-  removeSuffix(s, chars = {c})
-
-proc removeSuffix*(s: var string, suffix: string) {.
-  rtl, extern: "nsuRemoveSuffixString".} =
-  ## Remove the first matching suffix (in-place) from a string.
-  runnableExamples:
-     var answers = "yeses"
-     answers.removeSuffix("es")
-     doAssert answers == "yes"
-  var newLen = s.len
-  if s.endsWith(suffix):
-    newLen -= len(suffix)
-    s.setLen(newLen)
-
-proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
-  rtl, extern: "nsuRemovePrefixCharSet".} =
-  ## Removes all characters from `chars` from the start of the string `s`
-  ## (in-place).
+  ## If `leading` is true (default), leading `chars` are stripped.
+  ## If `trailing` is true (default), trailing `chars` are stripped.
+  ## If both are false, the string is returned unchanged.
   ##
+  ## See also:
+  ## * `stripLineEnd proc<#stripLineEnd,string>`_
   runnableExamples:
-     var userInput = "\r\n*~Hello World!"
-     userInput.removePrefix
-     doAssert userInput == "*~Hello World!"
-     userInput.removePrefix({'~', '*'})
-     doAssert userInput == "Hello World!"
+    let a = "  vhellov   "
+    let b = strip(a)
+    doAssert b == "vhellov"
 
-     var otherInput = "?!?Hello!?!"
-     otherInput.removePrefix({'!', '?'})
-     doAssert otherInput == "Hello!?!"
-  var start = 0
-  while start < s.len and s[start] in chars: start += 1
-  if start > 0: s.delete(0, start - 1)
+    doAssert a.strip(leading = false) == "  vhellov"
+    doAssert a.strip(trailing = false) == "vhellov   "
 
-proc removePrefix*(s: var string, c: char) {.
-  rtl, extern: "nsuRemovePrefixChar".} =
-  ## Removes all occurrences of a single character (in-place) from the start
-  ## of a string.
-  ##
-  runnableExamples:
-     var ident = "pControl"
-     ident.removePrefix('p')
-     doAssert ident == "Control"
-  removePrefix(s, chars = {c})
+    doAssert b.strip(chars = {'v'}) == "hello"
+    doAssert b.strip(leading = false, chars = {'v'}) == "vhello"
 
-proc removePrefix*(s: var string, prefix: string) {.
-  rtl, extern: "nsuRemovePrefixString".} =
-  ## Remove the first matching prefix (in-place) from a string.
-  ##
-  runnableExamples:
-     var answers = "yesyes"
-     answers.removePrefix("yes")
-     doAssert answers == "yes"
-  if s.startsWith(prefix):
-    s.delete(0, prefix.len - 1)
+    let c = "blaXbla"
+    doAssert c.strip(chars = {'b', 'a'}) == "laXbl"
+    doAssert c.strip(chars = {'b', 'a', 'l'}) == "X"
+
+  var
+    first = 0
+    last = len(s)-1
+  if leading:
+    while first <= last and s[first] in chars: inc(first)
+  if trailing:
+    while last >= 0 and s[last] in chars: dec(last)
+  result = substr(s, first, last)
 
 proc stripLineEnd*(s: var string) =
   ## Returns ``s`` stripped from one of these suffixes:
@@ -2520,6 +2688,7 @@ proc stripLineEnd*(s: var string) =
     s = "foo\r\n"
     s.stripLineEnd
     doAssert s == "foo"
+
   if s.len > 0:
     case s[^1]
     of '\n':
@@ -2532,6 +2701,331 @@ proc stripLineEnd*(s: var string) =
     else:
       discard
 
+
+iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
+  token: string, isSep: bool] =
+  ## Tokenizes the string `s` into substrings.
+  ##
+  ## Substrings are separated by a substring containing only `seps`.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   for word in tokenize("  this is an  example  "):
+  ##     writeLine(stdout, word)
+  ##
+  ## Results in:
+  ##
+  ## .. code-block:: nim
+  ##   ("  ", true)
+  ##   ("this", false)
+  ##   (" ", true)
+  ##   ("is", false)
+  ##   (" ", true)
+  ##   ("an", false)
+  ##   ("  ", true)
+  ##   ("example", false)
+  ##   ("  ", true)
+  var i = 0
+  while true:
+    var j = i
+    var isSep = j < s.len and s[j] in seps
+    while j < s.len and (s[j] in seps) == isSep: inc(j)
+    if j > i:
+      yield (substr(s, i, j-1), isSep)
+    else:
+      break
+    i = j
+
+
+
+
+
+# --------------------------------------------------------------------------
+# Deprecated procs
+
+{.push warning[Deprecated]: off.}
+proc editDistance*(a, b: string): int {.noSideEffect,
+  rtl, extern: "nsuEditDistance",
+  deprecated: "use editdistance.editDistanceAscii instead".} =
+  ## **Deprecated**: Use `editdistance module<editdistance.html>`_
+  ##
+  ## Returns the edit distance between `a` and `b`.
+  ##
+  ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
+  ## memory overhead.
+  var len1 = a.len
+  var len2 = b.len
+  if len1 > len2:
+    # make `b` the longer string
+    return editDistance(b, a)
+
+  # strip common prefix:
+  var s = 0
+  while s < len1 and a[s] == b[s]:
+    inc(s)
+    dec(len1)
+    dec(len2)
+  # strip common suffix:
+  while len1 > 0 and len2 > 0 and a[s+len1-1] == b[s+len2-1]:
+    dec(len1)
+    dec(len2)
+  # trivial cases:
+  if len1 == 0: return len2
+  if len2 == 0: return len1
+
+  # another special case:
+  if len1 == 1:
+    for j in s..s+len2-1:
+      if a[s] == b[j]: return len2 - 1
+    return len2
+
+  inc(len1)
+  inc(len2)
+  var half = len1 shr 1
+  # initalize first row:
+  #var row = cast[ptr array[0..high(int) div 8, int]](alloc(len2*sizeof(int)))
+  var row: seq[int]
+  newSeq(row, len2)
+  var e = s + len2 - 1 # end marker
+  for i in 1..len2 - half - 1: row[i] = i
+  row[0] = len1 - half - 1
+  for i in 1 .. len1 - 1:
+    var char1 = a[i + s - 1]
+    var char2p: int
+    var D, x: int
+    var p: int
+    if i >= len1 - half:
+      # skip the upper triangle:
+      var offset = i - len1 + half
+      char2p = offset
+      p = offset
+      var c3 = row[p] + ord(char1 != b[s + char2p])
+      inc(p)
+      inc(char2p)
+      x = row[p] + 1
+      D = x
+      if x > c3: x = c3
+      row[p] = x
+      inc(p)
+    else:
+      p = 1
+      char2p = 0
+      D = i
+      x = i
+    if i <= half + 1:
+      # skip the lower triangle:
+      e = len2 + i - half - 2
+    # main:
+    while p <= e:
+      dec(D)
+      var c3 = D + ord(char1 != b[char2p + s])
+      inc(char2p)
+      inc(x)
+      if x > c3: x = c3
+      D = row[p] + 1
+      if x > D: x = D
+      row[p] = x
+      inc(p)
+    # lower triangle sentinel:
+    if i <= half:
+      dec(D)
+      var c3 = D + ord(char1 != b[char2p + s])
+      inc(x)
+      if x > c3: x = c3
+      row[p] = x
+  result = row[e]
+{.pop.}
+
+proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
+                                      extern: "nsuIsNilOrEmpty",
+                                      deprecated: "use 'x.len == 0' instead".} =
+  ## **Deprecated**: use 'x.len == 0'
+  ##
+  ## Checks if `s` is nil or empty.
+  result = len(s) == 0
+
+proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
+  ## Checks if `s` is nil or consists entirely of whitespace characters.
+  result = true
+  for c in s:
+    if not c.isSpaceAscii():
+      return false
+
+template isImpl(call) =
+  if s.len == 0: return false
+  result = true
+  for c in s:
+    if not call(c): return false
+
+proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaAsciiStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is alphabetical.
+  ##
+  ## This checks a-z, A-Z ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## alphabetic and there is at least one character
+  ## in `s`.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isAlphaAscii("fooBar") == true
+    doAssert isAlphaAscii("fooBar1") == false
+    doAssert isAlphaAscii("foo Bar") == false
+  isImpl isAlphaAscii
+
+proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsAlphaNumericStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is alphanumeric.
+  ##
+  ## This checks a-z, A-Z, 0-9 ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## alpanumeric and there is at least one character
+  ## in `s`.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isAlphaNumeric("fooBar") == true
+    doAssert isAlphaNumeric("fooBar1") == true
+    doAssert isAlphaNumeric("foo Bar") == false
+  isImpl isAlphaNumeric
+
+proc isDigit*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsDigitStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is a numeric value.
+  ##
+  ## This checks 0-9 ASCII characters only.
+  ## Returns true if all characters in `s` are
+  ## numeric and there is at least one character
+  ## in `s`.
+  runnableExamples:
+    doAssert isDigit("1908") == true
+    doAssert isDigit("fooBar1") == false
+  isImpl isDigit
+
+proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
+  rtl, extern: "nsuIsSpaceAsciiStr",
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether or not `s` is completely whitespace.
+  ##
+  ## Returns true if all characters in `s` are whitespace
+  ## characters and there is at least one character in `s`.
+  runnableExamples:
+    doAssert isSpaceAscii("   ") == true
+    doAssert isSpaceAscii("") == false
+  isImpl isSpaceAscii
+
+template isCaseImpl(s, charProc, skipNonAlpha) =
+  var hasAtleastOneAlphaChar = false
+  if s.len == 0: return false
+  for c in s:
+    if skipNonAlpha:
+      var charIsAlpha = c.isAlphaAscii()
+      if not hasAtleastOneAlphaChar:
+        hasAtleastOneAlphaChar = charIsAlpha
+      if charIsAlpha and (not charProc(c)):
+        return false
+    else:
+      if not charProc(c):
+        return false
+  return if skipNonAlpha: hasAtleastOneAlphaChar else: true
+
+proc isLowerAscii*(s: string, skipNonAlpha: bool): bool {.
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether ``s`` is lower case.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
+  ## characters in ``s`` are lower case.  Returns false if none of the
+  ## characters in ``s`` are alphabetical.
+  ##
+  ## If ``skipNonAlpha`` is false, returns true only if all characters
+  ## in ``s`` are alphabetical and lower case.
+  ##
+  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
+  ## an empty string.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isLowerAscii("1foobar", false) == false
+    doAssert isLowerAscii("1foobar", true) == true
+    doAssert isLowerAscii("1fooBar", true) == false
+  isCaseImpl(s, isLowerAscii, skipNonAlpha)
+
+proc isUpperAscii*(s: string, skipNonAlpha: bool): bool {.
+  deprecated: "Deprecated since version 0.20 since its semantics are unclear".} =
+  ## **Deprecated**: Deprecated since version 0.20 since its semantics are unclear
+  ##
+  ## Checks whether ``s`` is upper case.
+  ##
+  ## This checks ASCII characters only.
+  ##
+  ## If ``skipNonAlpha`` is true, returns true if all alphabetical
+  ## characters in ``s`` are upper case.  Returns false if none of the
+  ## characters in ``s`` are alphabetical.
+  ##
+  ## If ``skipNonAlpha`` is false, returns true only if all characters
+  ## in ``s`` are alphabetical and upper case.
+  ##
+  ## For either value of ``skipNonAlpha``, returns false if ``s`` is
+  ## an empty string.
+  ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
+  runnableExamples:
+    doAssert isUpperAscii("1FOO", false) == false
+    doAssert isUpperAscii("1FOO", true) == true
+    doAssert isUpperAscii("1Foo", true) == false
+  isCaseImpl(s, isUpperAscii, skipNonAlpha)
+
+proc wordWrap*(s: string, maxLineWidth = 80,
+               splitLongWords = true,
+               seps: set[char] = Whitespace,
+               newLine = "\n"): string {.
+               noSideEffect, rtl, extern: "nsuWordWrap",
+               deprecated: "use wrapWords in std/wordwrap instead".} =
+  ## **Deprecated**: use wrapWords in std/wordwrap instead
+  ##
+  ## Word wraps `s`.
+  result = newStringOfCap(s.len + s.len shr 6)
+  var spaceLeft = maxLineWidth
+  var lastSep = ""
+  for word, isSep in tokenize(s, seps):
+    if isSep:
+      lastSep = word
+      spaceLeft = spaceLeft - len(word)
+      continue
+    if len(word) > spaceLeft:
+      if splitLongWords and len(word) > maxLineWidth:
+        result.add(substr(word, 0, spaceLeft-1))
+        var w = spaceLeft
+        var wordLeft = len(word) - spaceLeft
+        while wordLeft > 0:
+          result.add(newLine)
+          var L = min(maxLineWidth, wordLeft)
+          spaceLeft = maxLineWidth - L
+          result.add(substr(word, w, w+L-1))
+          inc(w, L)
+          dec(wordLeft, L)
+      else:
+        spaceLeft = maxLineWidth - len(word)
+        result.add(newLine)
+        result.add(word)
+    else:
+      spaceLeft = spaceLeft - len(word)
+      result.add(lastSep & word)
+      lastSep.setLen(0)
+
+
+
 when isMainModule:
   proc nonStaticTests =
     doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
deleted file mode 100644
index 638e71f04..000000000
--- a/lib/pure/subexes.nim
+++ /dev/null
@@ -1,406 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nim support for `substitution expressions`:idx: (`subex`:idx:).
-##
-## .. include:: ../../doc/subexes.txt
-##
-
-{.push debugger:off .} # the user does not want to trace a part
-                       # of the standard library!
-
-from strutils import parseInt, cmpIgnoreStyle, Digits
-include "system/inclrtl"
-import system/helpers2
-
-proc findNormalized(x: string, inArray: openarray[string]): int =
-  var i = 0
-  while i < high(inArray):
-    if cmpIgnoreStyle(x, inArray[i]) == 0: return i
-    inc(i, 2) # incrementing by 1 would probably lead to a
-              # security hole...
-  return -1
-
-type
-  SubexError* = object of ValueError ## exception that is raised for
-                                     ## an invalid subex
-
-proc raiseInvalidFormat(msg: string) {.noinline.} =
-  raise newException(SubexError, "invalid format string: " & msg)
-
-type
-  FormatParser = object {.pure, final.}
-    when defined(js):
-      f: string # we rely on the '\0' terminator
-                # which JS's native string doesn't have
-    else:
-      f: cstring
-    num, i, lineLen: int
-
-template call(x: untyped): untyped =
-  p.i = i
-  x
-  i = p.i
-
-template callNoLineLenTracking(x: untyped): untyped =
-  let oldLineLen = p.lineLen
-  p.i = i
-  x
-  i = p.i
-  p.lineLen = oldLineLen
-
-proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
-  const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
-  var i = p.i
-  var f = p.f
-  case f[i]
-  of '#':
-    result = p.num
-    inc i
-    inc p.num
-  of '1'..'9', '-':
-    var j = 0
-    var negative = f[i] == '-'
-    if negative: inc i
-    while f[i] in Digits:
-      j = j * 10 + ord(f[i]) - ord('0')
-      inc i
-    result = if not negative: j-1 else: a.len-j
-  of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
-    var name = ""
-    while f[i] in PatternChars:
-      name.add(f[i])
-      inc(i)
-    result = findNormalized(name, a)+1
-  of '$':
-    inc(i)
-    call:
-      result = getFormatArg(p, a)
-    result = parseInt(a[result])-1
-  else:
-    raiseInvalidFormat("'#', '$', number or identifier expected")
-  if result >=% a.len: raiseInvalidFormat(formatErrorIndexBound(result, a.len))
-  p.i = i
-
-proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {.
-  noSideEffect.}
-
-proc emitChar(p: var FormatParser, x: var string, ch: char) {.inline.} =
-  x.add(ch)
-  if ch == '\L': p.lineLen = 0
-  else: inc p.lineLen
-
-proc emitStrLinear(p: var FormatParser, x: var string, y: string) {.inline.} =
-  for ch in items(y): emitChar(p, x, ch)
-
-proc emitStr(p: var FormatParser, x: var string, y: string) {.inline.} =
-  x.add(y)
-  inc p.lineLen, y.len
-
-proc scanQuote(p: var FormatParser, x: var string, toAdd: bool) =
-  var i = p.i+1
-  var f = p.f
-  while true:
-    if f[i] == '\'':
-      inc i
-      if f[i] != '\'': break
-      inc i
-      if toAdd: emitChar(p, x, '\'')
-    elif f[i] == '\0': raiseInvalidFormat("closing \"'\" expected")
-    else:
-      if toAdd: emitChar(p, x, f[i])
-      inc i
-  p.i = i
-
-proc scanBranch(p: var FormatParser, a: openArray[string],
-                x: var string, choice: int) =
-  var i = p.i
-  var f = p.f
-  var c = 0
-  var elsePart = i
-  var toAdd = choice == 0
-  while true:
-    case f[i]
-    of ']': break
-    of '|':
-      inc i
-      elsePart = i
-      inc c
-      if toAdd: break
-      toAdd = choice == c
-    of '\'':
-      call: scanQuote(p, x, toAdd)
-    of '\0': raiseInvalidFormat("closing ']' expected")
-    else:
-      if toAdd:
-        if f[i] == '$':
-          inc i
-          call: scanDollar(p, a, x)
-        else:
-          emitChar(p, x, f[i])
-          inc i
-      else:
-        inc i
-  if not toAdd and choice >= 0:
-    # evaluate 'else' part:
-    var last = i
-    i = elsePart
-    while true:
-      case f[i]
-      of '|', ']': break
-      of '\'':
-        call: scanQuote(p, x, true)
-      of '$':
-        inc i
-        call: scanDollar(p, a, x)
-      else:
-        emitChar(p, x, f[i])
-        inc i
-    i = last
-  p.i = i+1
-
-proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
-  var slice = false
-  var i = p.i
-  var f = p.f
-
-  if f[i] == '{': inc i
-  else: raiseInvalidFormat("'{' expected")
-  if f[i] == '.' and f[i+1] == '.':
-    inc i, 2
-    slice = true
-  else:
-    call: result.x = getFormatArg(p, a)
-    if f[i] == '.' and f[i+1] == '.':
-      inc i, 2
-      slice = true
-  if slice:
-    if f[i] != '}':
-      call: result.y = getFormatArg(p, a)
-    else:
-      result.y = high(a)
-  else:
-    result.y = result.x
-  if f[i] != '}': raiseInvalidFormat("'}' expected")
-  inc i
-  p.i = i
-
-proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
-  var i = p.i
-  var f = p.f
-  case f[i]
-  of '$':
-    emitChar p, s, '$'
-    inc i
-  of '*':
-    for j in 0..a.high: emitStr p, s, a[j]
-    inc i
-  of '{':
-    call:
-      let (x, y) = scanSlice(p, a)
-    for j in x..y: emitStr p, s, a[j]
-  of '[':
-    inc i
-    var start = i
-    call: scanBranch(p, a, s, -1)
-    var x: int
-    if f[i] == '{':
-      inc i
-      call: x = getFormatArg(p, a)
-      if f[i] != '}': raiseInvalidFormat("'}' expected")
-      inc i
-    else:
-      call: x = getFormatArg(p, a)
-    var last = i
-    let choice = parseInt(a[x])
-    i = start
-    call: scanBranch(p, a, s, choice)
-    i = last
-  of '\'':
-    var sep = ""
-    callNoLineLenTracking: scanQuote(p, sep, true)
-    if f[i] == '~':
-      # $' '~{1..3}
-      # insert space followed by 1..3 if not empty
-      inc i
-      call:
-        let (x, y) = scanSlice(p, a)
-      var L = 0
-      for j in x..y: inc L, a[j].len
-      if L > 0:
-        emitStrLinear p, s, sep
-        for j in x..y: emitStr p, s, a[j]
-    else:
-      block StringJoin:
-        block OptionalLineLengthSpecifier:
-          var maxLen = 0
-          case f[i]
-          of '0'..'9':
-            while f[i] in Digits:
-              maxLen = maxLen * 10 + ord(f[i]) - ord('0')
-              inc i
-          of '$':
-            # do not skip the '$' here for `getFormatArg`!
-            call:
-              maxLen = getFormatArg(p, a)
-          else: break OptionalLineLengthSpecifier
-          var indent = ""
-          case f[i]
-          of 'i':
-            inc i
-            callNoLineLenTracking: scanQuote(p, indent, true)
-
-            call:
-              let (x, y) = scanSlice(p, a)
-            if maxLen < 1: emitStrLinear(p, s, indent)
-            var items = 1
-            emitStr p, s, a[x]
-            for j in x+1..y:
-              emitStr p, s, sep
-              if items >= maxLen:
-                emitStrLinear p, s, indent
-                items = 0
-              emitStr p, s, a[j]
-              inc items
-          of 'c':
-            inc i
-            callNoLineLenTracking: scanQuote(p, indent, true)
-
-            call:
-              let (x, y) = scanSlice(p, a)
-            if p.lineLen + a[x].len > maxLen: emitStrLinear(p, s, indent)
-            emitStr p, s, a[x]
-            for j in x+1..y:
-              emitStr p, s, sep
-              if p.lineLen + a[j].len > maxLen: emitStrLinear(p, s, indent)
-              emitStr p, s, a[j]
-
-          else: raiseInvalidFormat("unit 'c' (chars) or 'i' (items) expected")
-          break StringJoin
-
-        call:
-          let (x, y) = scanSlice(p, a)
-        emitStr p, s, a[x]
-        for j in x+1..y:
-          emitStr p, s, sep
-          emitStr p, s, a[j]
-  else:
-    call:
-      var x = getFormatArg(p, a)
-    emitStr p, s, a[x]
-  p.i = i
-
-
-type
-  Subex* = distinct string ## string that contains a substitution expression
-
-proc subex*(s: string): Subex =
-  ## constructs a *substitution expression* from `s`. Currently this performs
-  ## no syntax checking but this may change in later versions.
-  result = Subex(s)
-
-proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
-           noSideEffect, rtl, extern: "nfrmtAddf".} =
-  ## The same as ``add(s, formatstr % a)``, but more efficient.
-  var p: FormatParser
-  p.f = formatstr.string
-  var i = 0
-  while i < len(formatstr.string):
-    if p.f[i] == '$':
-      inc i
-      call: scanDollar(p, a, s)
-    else:
-      emitChar(p, s, p.f[i])
-      inc(i)
-
-proc `%` *(formatstr: Subex, a: openarray[string]): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatOpenArray".} =
-  ## The `substitution`:idx: operator performs string substitutions in
-  ## `formatstr` and returns a modified `formatstr`. This is often called
-  ## `string interpolation`:idx:.
-  ##
-  result = newStringOfCap(formatstr.string.len + a.len shl 4)
-  addf(result, formatstr, a)
-
-proc `%` *(formatstr: Subex, a: string): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatSingleElem".} =
-  ## This is the same as ``formatstr % [a]``.
-  result = newStringOfCap(formatstr.string.len + a.len)
-  addf(result, formatstr, [a])
-
-proc format*(formatstr: Subex, a: varargs[string, `$`]): string {.noSideEffect,
-  rtl, extern: "nfrmtFormatVarargs".} =
-  ## The `substitution`:idx: operator performs string substitutions in
-  ## `formatstr` and returns a modified `formatstr`. This is often called
-  ## `string interpolation`:idx:.
-  ##
-  result = newStringOfCap(formatstr.string.len + a.len shl 4)
-  addf(result, formatstr, a)
-
-{.pop.}
-
-when isMainModule:
-  from strutils import replace
-
-  proc `%`(formatstr: string, a: openarray[string]): string =
-    result = newStringOfCap(formatstr.len + a.len shl 4)
-    addf(result, formatstr.Subex, a)
-
-  proc `%`(formatstr: string, a: string): string =
-    result = newStringOfCap(formatstr.len + a.len)
-    addf(result, formatstr.Subex, [a])
-
-
-  doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
-  doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
-           "The cat eats fish."
-
-
-  doAssert "$[abc|def]# $3 $# $#" % ["17", "b", "c"] == "def c b c"
-  doAssert "$[abc|def]# $3 $# $#" % ["1", "b", "c"] == "def c b c"
-  doAssert "$[abc|def]# $3 $# $#" % ["0", "b", "c"] == "abc c b c"
-  doAssert "$[abc|def|]# $3 $# $#" % ["17", "b", "c"] == " c b c"
-
-  doAssert "$[abc|def|]# $3 $# $#" % ["-9", "b", "c"] == " c b c"
-  doAssert "$1($', '{2..})" % ["f", "a", "b"] == "f(a, b)"
-
-  doAssert "$[$1($', '{2..})|''''|fg'$3']1" % ["7", "a", "b"] == "fg$3"
-
-  doAssert "$[$#($', '{#..})|''''|$3]1" % ["0", "a", "b"] == "0(a, b)"
-  doAssert "$' '~{..}" % "" == ""
-  doAssert "$' '~{..}" % "P0" == " P0"
-  doAssert "${$1}" % "1" == "1"
-  doAssert "${$$-1} $$1" % "1" == "1 $1"
-
-  doAssert(("$#($', '10c'\n    '{#..})" % ["doAssert", "longishA", "longish"]).replace(" \n", "\n") ==
-           """doAssert(
-    longishA,
-    longish)""")
-
-  doAssert(("type MyEnum* = enum\n  $', '2i'\n  '{..}" % ["fieldA",
-    "fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"]).replace(" \n", "\n") ==
-    strutils.unindent("""
-      type MyEnum* = enum
-        fieldA, fieldB,
-        FiledClkad, fieldD,
-        fieldE, longishFieldName""", 6))
-
-  doAssert subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
-
-  doAssert subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
-
-  doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
-
-  doAssert((subex("type\n  Enum = enum\n    $', '40c'\n    '{..}") % [
-    "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]).replace(" \n", "\n") ==
-    strutils.unindent("""
-      type
-        Enum = enum
-          fieldNameA, fieldNameB, fieldNameC,
-          fieldNameD""", 6))
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 35dc2483c..2b3c08d0d 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -37,7 +37,7 @@ type
 
 var gTerm {.threadvar.}: PTerminal
 
-proc newTerminal(): PTerminal
+proc newTerminal(): PTerminal {.gcsafe.}
 
 proc getTerminal(): PTerminal {.inline.} =
   if isNil(gTerm):
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 010551b5a..0104a97c1 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -1,35 +1,41 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2017 Nim contributors
+#        (c) Copyright 2018 Nim contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
 ##[
-  This module contains routines and types for dealing with time using a proleptic Gregorian calendar.
-  It's also available for the `JavaScript target <backends.html#the-javascript-target>`_.
+  The ``times`` module contains routines and types for dealing with time using
+  the `proleptic Gregorian calendar<https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar>`_.
+  It's also available for the
+  `JavaScript target <backends.html#backends-the-javascript-target>`_.
 
-  Although the types use nanosecond time resolution, the underlying resolution used by ``getTime()``
-  depends on the platform and backend (JS is limited to millisecond precision).
+  Although the ``times`` module support nanosecond time resolution, the
+  resolution used by ``getTime()`` depends on the platform and backend
+  (JS is limited to millisecond precision).
 
   Examples:
 
   .. code-block:: nim
-
     import times, os
+    # Simple benchmarking
     let time = cpuTime()
-
-    sleep(100)   # replace this with something to be timed
+    sleep(100)   # Replace this with something to be timed
     echo "Time taken: ", cpuTime() - time
 
-    echo "My formatted time: ", format(now(), "d MMMM yyyy HH:mm")
-    echo "Using predefined formats: ", getClockStr(), " ", getDateStr()
+    # Current date & time
+    let now1 = now()     # Current timestamp as a DateTime in local time
+    let now2 = now().utc # Current timestamp as a DateTime in UTC
+    let now3 = getTime() # Current timestamp as a Time
 
-    echo "cpuTime()  float value: ", cpuTime()
-    echo "An hour from now      : ", now() + 1.hours
-    echo "An hour from (UTC) now: ", getTime().utc + initDuration(hours = 1)
+    # Arithmetic using Duration
+    echo "One hour from now      : ", now() + initDuration(hours = 1)
+    # Arithmetic using TimeInterval
+    echo "One year from now      : ", now() + 1.years
+    echo "One month from now     : ", now() + 1.months
 
   Parsing and Formatting Dates
   ----------------------------
@@ -97,14 +103,14 @@
                                                                                                     | ``24 AD -> 24``
                                                                                                     | ``24 BC -> -23``
                                                                                                     | ``12345 AD -> 12345``
-  ``z``          Displays the timezone offset from UTC.                                             | ``GMT+7 -> +7``
-                                                                                                    | ``GMT-5 -> -5``
-  ``zz``         Same as above but with leading 0.                                                  | ``GMT+7 -> +07``
-                                                                                                    | ``GMT-5 -> -05``
-  ``zzz``        Same as above but with ``:mm`` where *mm* represents minutes.                      | ``GMT+7 -> +07:00``
-                                                                                                    | ``GMT-5 -> -05:00``
-  ``zzzz``       Same as above but with ``:ss`` where *ss* represents seconds.                      | ``GMT+7 -> +07:00:00``
-                                                                                                    | ``GMT-5 -> -05:00:00``
+  ``z``          Displays the timezone offset from UTC.                                             | ``UTC+7 -> +7``
+                                                                                                    | ``UTC-5 -> -5``
+  ``zz``         Same as above but with leading 0.                                                  | ``UTC+7 -> +07``
+                                                                                                    | ``UTC-5 -> -05``
+  ``zzz``        Same as above but with ``:mm`` where *mm* represents minutes.                      | ``UTC+7 -> +07:00``
+                                                                                                    | ``UTC-5 -> -05:00``
+  ``zzzz``       Same as above but with ``:ss`` where *ss* represents seconds.                      | ``UTC+7 -> +07:00:00``
+                                                                                                    | ``UTC-5 -> -05:00:00``
   ``g``          Era: AD or BC                                                                      | ``300 AD -> AD``
                                                                                                     | ``300 BC -> BC``
   ``fff``        Milliseconds display                                                               | ``1000000 nanoseconds -> 1``
@@ -117,72 +123,141 @@
   inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
   ``,``. A literal ``'`` can be specified with ``''``.
 
-  However you don't need to necessarily separate format patterns, a
+  However you don't need to necessarily separate format patterns, an
   unambiguous format string like ``yyyyMMddhhmmss`` is valid too (although
   only for years in the range 1..9999).
-]##
-
 
-{.push debugger:off.} # the user does not want to trace a part
-                      # of the standard library!
+  Duration vs TimeInterval
+  ----------------------------
+  The ``times`` module exports two similiar types that are both used to
+  represent some amount of time: ``Duration`` and ``TimeInterval``.
+  This section explains how they differ and when one should be prefered over the
+  other (short answer: use ``Duration`` unless support for months and years is
+  needed).
+
+  Duration
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  A ``Duration`` represents a duration of time stored as seconds and
+  nanoseconds. A ``Duration`` is always fully normalized, so
+``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
+
+  Arithmetics with a ``Duration`` is very fast, especially when used with the
+  ``Time`` type, since it only involves basic arithmetic. Because ``Duration``
+  is more performant and easier to understand it should generally prefered.
+
+  TimeInterval
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  A ``TimeInterval`` represents some amount of time expressed in calendar
+  units, for example "1 year and 2 days". Since some units cannot be
+  normalized (the length of a year is different for leap years for example),
+  the ``TimeInterval`` type uses seperate fields for every unit. The
+  ``TimeInterval``'s returned form the this module generally don't normalize
+  **anything**, so even units that could be normalized (like seconds,
+  milliseconds and so on) are left untouched.
+
+  Arithmetics with a ``TimeInterval`` can be very slow, because it requires
+  timezone information.
+
+  Since it's slower and more complex, the ``TimeInterval`` type should be
+  avoided unless the program explicitly needs the features it offers that
+  ``Duration`` doesn't have.
+
+  How long is a day?
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  It should be especially noted that the handling of days differs between
+  ``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
+  as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
+
+  As an example, consider the amount of time between these two timestamps, both
+  in the same timezone:
+
+    - 2018-03-25T12:00+02:00
+    - 2018-03-26T12:00+01:00
+
+  If only the date & time is considered, it appears that exatly one day has
+  passed. However, the UTC offsets are different, which means that the
+  UTC offset was changed somewhere between. This happens twice each year for
+  timezones that use daylight savings time. Because of this change, the amount
+  of time that has passed is actually 25 hours.
+
+  The ``TimeInterval`` type uses calendar units, and will say that exactly one
+  day has passed. The ``Duration`` type on the other hand normalizes everything
+  to seconds, and will therefore say that 90000 seconds has passed, which is
+  the same as 25 hours.
+]##
 
-import
-  strutils, parseutils, algorithm, math, options, strformat
+import strutils, algorithm, math, options, strformat
 
 include "system/inclrtl"
 
-# This is really bad, but overflow checks are broken badly for
-# ints on the JS backend. See #6752.
 when defined(JS):
+  import jscore
+
+  # This is really bad, but overflow checks are broken badly for
+  # ints on the JS backend. See #6752.
   {.push overflowChecks: off.}
   proc `*`(a, b: int64): int64 =
-    system.`* `(a, b)
+    system.`*`(a, b)
   proc `*`(a, b: int): int =
-    system.`* `(a, b)
+    system.`*`(a, b)
   proc `+`(a, b: int64): int64 =
-    system.`+ `(a, b)
+    system.`+`(a, b)
   proc `+`(a, b: int): int =
-    system.`+ `(a, b)
+    system.`+`(a, b)
   proc `-`(a, b: int64): int64 =
-    system.`- `(a, b)
+    system.`-`(a, b)
   proc `-`(a, b: int): int =
-    system.`- `(a, b)
+    system.`-`(a, b)
   proc inc(a: var int, b: int) =
     system.inc(a, b)
   proc inc(a: var int64, b: int) =
     system.inc(a, b)
   {.pop.}
 
-when defined(posix):
+elif defined(posix):
   import posix
 
   type CTime = posix.Time
 
   var
     realTimeClockId {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
-    cpuClockId {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid
+    cpuClockId
+      {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "<time.h>".}: Clockid
 
-  proc gettimeofday(tp: var Timeval, unused: pointer = nil) {.
-    importc: "gettimeofday", header: "<sys/time.h>".}
+  proc gettimeofday(tp: var Timeval, unused: pointer = nil)
+      {.importc: "gettimeofday", header: "<sys/time.h>".}
 
   when not defined(freebsd) and not defined(netbsd) and not defined(openbsd):
     var timezone {.importc, header: "<time.h>".}: int
-    tzset()
+    when not defined(valgrind_workaround_10121):
+      tzset()
 
 elif defined(windows):
-  import winlean
+  import winlean, std/time_t
+
+  type CTime = time_t.Time
 
-  when defined(i386) and defined(gcc):
-    type CTime {.importc: "time_t", header: "<time.h>".} = distinct int32
-  else:
-    # newest version of Visual C++ defines time_t to be of 64 bits
-    type CTime {.importc: "time_t", header: "<time.h>".} = distinct int64
   # visual c's c runtime exposes these under a different name
   var timezone {.importc: "_timezone", header: "<time.h>".}: int
 
+  type
+    Tm {.importc: "struct tm", header: "<time.h>", final, pure.} = object
+      tm_sec*: cint   ## Seconds [0,60].
+      tm_min*: cint   ## Minutes [0,59].
+      tm_hour*: cint  ## Hour [0,23].
+      tm_mday*: cint  ## Day of month [1,31].
+      tm_mon*: cint   ## Month of year [0,11].
+      tm_year*: cint  ## Years since 1900.
+      tm_wday*: cint  ## Day of week [0,6] (Sunday =0).
+      tm_yday*: cint  ## Day of year [0,365].
+      tm_isdst*: cint ## Daylight Savings flag.
+
+  proc localtime(a1: var CTime): ptr Tm {.importc, header: "<time.h>".}
+
 type
-  Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
-                ## the month number in the range ``[1..12]``.
+  Month* = enum ## Represents a month. Note that the enum starts at ``1``,
+                ## so ``ord(month)`` will give the month number in the
+                ## range ``1..12``.
     mJan = (1, "January")
     mFeb = "February"
     mMar = "March"
@@ -216,13 +291,19 @@ type
     seconds: int64
     nanosecond: NanosecondRange
 
-  DateTime* = object of RootObj ## Represents a time in different parts.
-                                ## Although this type can represent leap
-                                ## seconds, they are generally not supported
-                                ## in this module. They are not ignored,
-                                ## but the ``DateTime``'s returned by
-                                ## procedures in this module will never have
-                                ## a leap second.
+  DateTime* = object of RootObj ## \
+      ## Represents a time in different parts. Although this type can represent
+      ## leap seconds, they are generally not supported in this module. They are
+      ## not ignored, but the ``DateTime``'s returned by procedures in this
+      ## module will never have a leap second.
+      ##
+      ## **Warning**: even though the fields of ``DateTime`` are exported,
+      ## they should never be mutated directly. Doing so is unsafe and will
+      ## result in the ``DateTime`` ending up in an invalid state.
+      ##
+      ## Instead of mutating the fields directly, use the ``Duration``
+      ## and ``TimeInterval`` types for arithmetic and use the ``initDateTime``
+      ## procedure for changing a specific field.
     nanosecond*: NanosecondRange ## The number of nanoseconds after the second,
                                  ## in the range 0 to 999_999_999.
     second*: SecondRange      ## The number of seconds after the minute,
@@ -233,27 +314,48 @@ type
     hour*: HourRange          ## The number of hours past midnight,
                               ## in the range 0 to 23.
     monthday*: MonthdayRange  ## The day of the month, in the range 1 to 31.
-    month*: Month             ## The current month.
-    year*: int                ## The current year, using astronomical year numbering
-                              ## (meaning that before year 1 is year 0, then year -1 and so on).
-    weekday*: WeekDay         ## The current day of the week.
+    month*: Month             ## The month.
+    year*: int                ## The year, using astronomical year numbering
+                              ## (meaning that before year 1 is year 0,
+                              ## then year -1 and so on).
+    weekday*: WeekDay         ## The day of the week.
     yearday*: YeardayRange    ## The number of days since January 1,
                               ## in the range 0 to 365.
     isDst*: bool              ## Determines whether DST is in effect.
                               ## Always false for the JavaScript backend.
-    timezone*: Timezone       ## The timezone represented as an implementation of ``Timezone``.
-    utcOffset*: int           ## The offset in seconds west of UTC, including any offset due to DST.
-                              ## Note that the sign of this number is the opposite
-                              ## of the one in a formatted offset string like ``+01:00``
-                              ## (which would be parsed into the UTC offset ``-3600``).
-
-  TimeInterval* = object ## Represents a non-fixed duration of time. Can be used to add and subtract
-                         ## non-fixed time units from a ``DateTime`` or ``Time``.
-                         ## ``TimeInterval`` doesn't represent a fixed duration of time,
-                         ## since the duration of some units depend on the context (e.g a year
-                         ## can be either 365 or 366 days long). The non-fixed time units are years,
-                         ## months and days.
+    timezone*: Timezone       ## The timezone represented as an implementation
+                              ## of ``Timezone``.
+    utcOffset*: int           ## The offset in seconds west of UTC, including
+                              ## any offset due to DST. Note that the sign of
+                              ## this number is the opposite of the one in a
+                              ## formatted offset string like ``+01:00`` (which
+                              ## would be equivalent to the UTC offset
+                              ## ``-3600``).
+
+  Duration* = object ## Represents a fixed duration of time, meaning a duration
+                     ## that has constant length independent of the context.
+    seconds: int64
+    nanosecond: NanosecondRange
 
+  TimeUnit* = enum ## Different units of time.
+    Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days,
+    Weeks, Months, Years
+
+  FixedTimeUnit* = range[Nanoseconds..Weeks] ## \
+      ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
+      ## These are the units that can be represented by a ``Duration``.
+
+  TimeInterval* = object ## \
+      ## Represents a non-fixed duration of time. Can be used to add and
+      ## subtract non-fixed time units from a ``DateTime`` or ``Time``.
+      ## Note that ``TimeInterval`` doesn't represent a fixed duration of time,
+      ## since the duration of some units depend on the context (e.g a year
+      ## can be either 365 or 366 days long). The non-fixed time units are
+      ## years, months, days and week.
+      ##
+      ## Note that ``TimeInterval``'s returned from the ``times`` module are
+      ## never normalized. If you want to normalize a time unit, ``Duration``
+      ## should be used instead.
     nanoseconds*: int  ## The number of nanoseconds
     microseconds*: int ## The number of microseconds
     milliseconds*: int ## The number of milliseconds
@@ -265,19 +367,6 @@ type
     months*: int       ## The number of months
     years*: int        ## The number of years
 
-  Duration* = object ## Represents a fixed duration of time.
-                     ## Uses the same time resolution as ``Time``.
-                     ## This type should be prefered over ``TimeInterval`` unless
-                     ## non-static time units is needed.
-    seconds: int64
-    nanosecond: NanosecondRange
-
-  TimeUnit* = enum ## Different units of time.
-    Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes, Hours, Days, Weeks, Months, Years
-
-  FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
-                                             ## These are the units that can be represented by a ``Duration``.
-
   Timezone* = ref object ## \
       ## Timezone interface for supporting ``DateTime``'s of arbritary
       ## timezones. The ``times`` module only supplies implementations for the
@@ -304,7 +393,6 @@ const
   secondsInMin = 60
   secondsInHour = 60*60
   secondsInDay = 60*60*24
-  minutesInHour = 60
   rateDiff = 10000000'i64 # 100 nsecs
   # The number of hectonanoseconds between 1601/01/01 (windows epoch)
   # and 1970/01/01 (unix epoch).
@@ -321,8 +409,10 @@ const unitWeights: array[FixedTimeUnit, int64] = [
   7 * secondsInDay * 1e9.int64,
 ]
 
-proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T {.inline.} =
+proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T
+    {.inline.} =
   ## Convert a quantity of some duration unit to another duration unit.
+  ## This proc only deals with integers, so the result might be truncated.
   runnableExamples:
     doAssert convert(Days, Hours, 2) == 48
     doAssert convert(Days, Weeks, 13) == 1 # Truncated
@@ -344,21 +434,41 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
   result.nanosecond = nanosecond.int
 
 # Forward declarations
-proc utcTzInfo(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZonedTimeFromTime(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc utcTzInfo(time: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
+proc localZonedTimeFromTime(time: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
+proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime
+    {.tags: [], raises: [], benign.}
 proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
-  {.tags: [], raises: [], benign noSideEffect.}
-
-proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration
-  {.tags: [], raises: [], benign noSideEffect.}
+    {.tags: [], raises: [], benign, noSideEffect.}
 
 proc nanosecond*(time: Time): NanosecondRange =
   ## Get the fractional part of a ``Time`` as the number
   ## of nanoseconds of the second.
   time.nanosecond
 
+proc initDuration*(nanoseconds, microseconds, milliseconds,
+                   seconds, minutes, hours, days, weeks: int64 = 0): Duration =
+  ## Create a new duration.
+  runnableExamples:
+    let dur = initDuration(seconds = 1, milliseconds = 1)
+    doAssert dur.milliseconds == 1
+    doAssert dur.seconds == 1
+
+  let seconds = convert(Weeks, Seconds, weeks) +
+    convert(Days, Seconds, days) +
+    convert(Minutes, Seconds, minutes) +
+    convert(Hours, Seconds, hours) +
+    convert(Seconds, Seconds, seconds) +
+    convert(Milliseconds, Seconds, milliseconds) +
+    convert(Microseconds, Seconds, microseconds) +
+    convert(Nanoseconds, Seconds, nanoseconds)
+  let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
+    convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
+    nanoseconds mod 1_000_000_000).int
+  # Nanoseconds might be negative so we must normalize.
+  result = normalize[Duration](seconds, nanoseconds)
 
 proc weeks*(dur: Duration): int64 {.inline.} =
   ## Number of whole weeks represented by the duration.
@@ -411,9 +521,10 @@ proc fractional*(dur: Duration): Duration {.inline.} =
     doAssert dur.fractional == initDuration(nanoseconds = 5)
   initDuration(nanoseconds = dur.nanosecond)
 
-
-proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} =
-  ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) to a ``Time``.
+proc fromUnix*(unix: int64): Time
+    {.benign, tags: [], raises: [], noSideEffect.} =
+  ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``)
+  ## to a ``Time``.
   runnableExamples:
     doAssert $fromUnix(0).utc == "1970-01-01T00:00:00Z"
   initTime(unix, 0)
@@ -425,15 +536,16 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
   t.seconds
 
 proc fromWinTime*(win: int64): Time =
-  ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``)
-  ## to a ``Time``.
+  ## Convert a Windows file time (100-nanosecond intervals since
+  ## ``1601-01-01T00:00:00Z``) to a ``Time``.
   const hnsecsPerSec = convert(Seconds, Nanoseconds, 1) div 100
   let nanos = floorMod(win, hnsecsPerSec) * 100
   let seconds = floorDiv(win - epochDiff, hnsecsPerSec)
   result = initTime(seconds, nanos)
 
 proc toWinTime*(t: Time): int64 =
-  ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``).
+  ## Convert ``t`` to a Windows file time (100-nanosecond intervals
+  ## since ``1601-01-01T00:00:00Z``).
   result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
 
 proc isLeapYear*(year: int): bool =
@@ -441,7 +553,7 @@ proc isLeapYear*(year: int): bool =
   year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0)
 
 proc getDaysInMonth*(month: Month, year: int): int =
-  ## Get the number of days in a ``month`` of a ``year``.
+  ## Get the number of days in ``month`` of ``year``.
   # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
   case month
   of mFeb: result = if isLeapYear(year): 29 else: 28
@@ -452,15 +564,18 @@ proc getDaysInYear*(year: int): int =
   ## Get the number of days in a ``year``
   result = 365 + (if isLeapYear(year): 1 else: 0)
 
-proc assertValidDate(monthday: MonthdayRange, month: Month, year: int) {.inline.} =
+proc assertValidDate(monthday: MonthdayRange, month: Month, year: int)
+    {.inline.} =
   assert monthday <= getDaysInMonth(month, year),
-    $year & "-" & intToStr(ord(month), 2) & "-" & $monthday & " is not a valid date"
+    $year & "-" & intToStr(ord(month), 2) & "-" & $monthday &
+      " is not a valid date"
 
 proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
   ## Get the epoch day from a year/month/day date.
-  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
-  assertValidDate monthday, month, year
+  ## The epoch day is the number of days since 1970/01/01
+  ## (it might be negative).
   # Based on http://howardhinnant.github.io/date_algorithms.html
+  assertValidDate monthday, month, year
   var (y, m, d) = (year, ord(month), monthday.int)
   if m <= 2:
     y.dec
@@ -471,9 +586,11 @@ proc toEpochDay(monthday: MonthdayRange, month: Month, year: int): int64 =
   let doe = yoe * 365 + yoe div 4 - yoe div 100 + doy
   return era * 146097 + doe - 719468
 
-proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month, year: int] =
+proc fromEpochDay(epochday: int64):
+    tuple[monthday: MonthdayRange, month: Month, year: int] =
   ## Get the year/month/day date from a epoch day.
-  ## The epoch day is the number of days since 1970/01/01 (it might be negative).
+  ## The epoch day is the number of days since 1970/01/01
+  ## (it might be negative).
   # Based on http://howardhinnant.github.io/date_algorithms.html
   var z = epochday
   z.inc 719468
@@ -487,19 +604,23 @@ proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month,
   let m = mp + (if mp < 10: 3 else: -9)
   return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
 
-proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
+proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int):
+    YeardayRange {.tags: [], raises: [], benign.} =
   ## Returns the day of the year.
   ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``.
   assertValidDate monthday, month, year
-  const daysUntilMonth:     array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
-  const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
+  const daysUntilMonth: array[Month, int] =
+    [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
+  const daysUntilMonthLeap: array[Month, int] =
+    [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
 
   if isLeapYear(year):
     result = daysUntilMonthLeap[month] + monthday - 1
   else:
     result = daysUntilMonth[month] + monthday - 1
 
-proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
+proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay
+    {.tags: [], raises: [], benign.} =
   ## Returns the day of the week enum from day, month and year.
   ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``.
   assertValidDate monthday, month, year
@@ -511,8 +632,7 @@ proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.
   # so we must correct for the WeekDay type.
   result = if wd == 0: dSun else: WeekDay(wd - 1)
 
-
-{. pragma: operator, rtl, noSideEffect, benign .}
+{.pragma: operator, rtl, noSideEffect, benign.}
 
 template subImpl[T: Duration|Time](a: Duration|Time, b: Duration|Time): T =
   normalize[T](a.seconds - b.seconds, a.nanosecond - b.nanosecond)
@@ -530,28 +650,6 @@ template lqImpl(a: Duration|Time, b: Duration|Time): bool =
 
 template eqImpl(a: Duration|Time, b: Duration|Time): bool =
   a.seconds == b.seconds and a.nanosecond == b.nanosecond
-
-proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration =
-  runnableExamples:
-    let dur = initDuration(seconds = 1, milliseconds = 1)
-    doAssert dur.milliseconds == 1
-    doAssert dur.seconds == 1
-
-  let seconds = convert(Weeks, Seconds, weeks) +
-    convert(Days, Seconds, days) +
-    convert(Minutes, Seconds, minutes) +
-    convert(Hours, Seconds, hours) +
-    convert(Seconds, Seconds, seconds) +
-    convert(Milliseconds, Seconds, milliseconds) +
-    convert(Microseconds, Seconds, microseconds) +
-    convert(Nanoseconds, Seconds, nanoseconds)
-  let nanoseconds = (convert(Milliseconds, Nanoseconds, milliseconds mod 1000) +
-    convert(Microseconds, Nanoseconds, microseconds mod 1_000_000) +
-    nanoseconds mod 1_000_000_000).int
-  # Nanoseconds might be negative so we must normalize.
-  result = normalize[Duration](seconds, nanoseconds)
-
 const DurationZero* = initDuration() ## \
   ## Zero value for durations. Useful for comparisons.
   ##
@@ -568,7 +666,7 @@ proc toParts*(dur: Duration): DurationParts =
   ##
   ## This procedure is useful for converting ``Duration`` values to strings.
   runnableExamples:
-    var dp = toParts(initDuration(weeks=2, days=1))
+    var dp = toParts(initDuration(weeks = 2, days = 1))
     doAssert dp[Days] == 1
     doAssert dp[Weeks] == 2
     dp = toParts(initDuration(days = -1))
@@ -620,12 +718,14 @@ proc humanizeParts(parts: seq[string]): string =
     result.add "and " & parts[high(parts)]
 
 proc `$`*(dur: Duration): string =
-  ## Human friendly string representation of ``Duration``.
+  ## Human friendly string representation of a ``Duration``.
   runnableExamples:
     doAssert $initDuration(seconds = 2) == "2 seconds"
     doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days"
-    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) == "1 hour, 2 minutes, and 3 seconds"
-    doAssert $initDuration(milliseconds = -1500) == "-1 second and -500 milliseconds"
+    doAssert $initDuration(hours = 1, minutes = 2, seconds = 3) ==
+      "1 hour, 2 minutes, and 3 seconds"
+    doAssert $initDuration(milliseconds = -1500) ==
+      "-1 second and -500 milliseconds"
   var parts = newSeq[string]()
   var numParts = toParts(dur)
 
@@ -663,7 +763,7 @@ proc `<`*(a, b: Duration): bool {.operator.} =
   ## Use ``abs(a) < abs(b)`` to compare the absolute
   ## duration.
   runnableExamples:
-    doAssert initDuration(seconds =  1) < initDuration(seconds = 2)
+    doAssert initDuration(seconds = 1) < initDuration(seconds = 2)
     doAssert initDuration(seconds = -2) < initDuration(seconds = 1)
   ltImpl(a, b)
 
@@ -673,23 +773,25 @@ proc `<=`*(a, b: Duration): bool {.operator.} =
 proc `==`*(a, b: Duration): bool {.operator.} =
   eqImpl(a, b)
 
-proc `*`*(a: int64, b: Duration): Duration {.operator} =
+proc `*`*(a: int64, b: Duration): Duration {.operator.} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert 5 * initDuration(seconds = 1) == initDuration(seconds = 5)
   normalize[Duration](a * b.seconds, a * b.nanosecond)
 
-proc `*`*(a: Duration, b: int64): Duration {.operator} =
+proc `*`*(a: Duration, b: int64): Duration {.operator.} =
   ## Multiply a duration by some scalar.
   runnableExamples:
     doAssert initDuration(seconds = 1) * 5 == initDuration(seconds = 5)
   b * a
 
-proc `div`*(a: Duration, b: int64): Duration {.operator} =
+proc `div`*(a: Duration, b: int64): Duration {.operator.} =
   ## Integer division for durations.
   runnableExamples:
-    doAssert initDuration(seconds = 3) div 2 == initDuration(milliseconds = 1500)
-    doAssert initDuration(nanoseconds = 3) div 2 == initDuration(nanoseconds = 1)
+    doAssert initDuration(seconds = 3) div 2 ==
+      initDuration(milliseconds = 1500)
+    doAssert initDuration(nanoseconds = 3) div 2 ==
+      initDuration(nanoseconds = 1)
   let carryOver = convert(Seconds, Nanoseconds, a.seconds mod b)
   normalize[Duration](a.seconds div b, (a.nanosecond + carryOver) div b)
 
@@ -718,7 +820,7 @@ proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
   ## Returns true iff ``a < b``, that is iff a happened before b.
   ltImpl(a, b)
 
-proc `<=` * (a, b: Time): bool {.operator, extern: "ntLeTime".} =
+proc `<=`*(a, b: Time): bool {.operator, extern: "ntLeTime".} =
   ## Returns true iff ``a <= b``.
   lqImpl(a, b)
 
@@ -787,8 +889,10 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
 
 proc newTimezone*(
       name: string,
-      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.},
-      zonedTimeFromAdjTimeImpl:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
+      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime
+          {.tags: [], raises: [], benign.},
+      zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime
+          {.tags: [], raises: [], benign.}
     ): Timezone =
   ## Create a new ``Timezone``.
   ##
@@ -851,11 +955,13 @@ proc `==`*(zone1, zone2: Timezone): bool =
     doAssert local() != utc()
   zone1.name == zone2.name
 
-proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
+proc inZone*(time: Time, zone: Timezone): DateTime
+    {.tags: [], raises: [], benign.} =
   ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone.
   result = initDateTime(zone.zonedTimeFromTime(time), zone)
 
-proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
+proc inZone*(dt: DateTime, zone: Timezone): DateTime
+    {.tags: [], raises: [], benign.} =
   ## Returns a ``DateTime`` representing the same point in time as ``dt`` but
   ## using ``zone`` as the timezone.
   dt.toTime.inZone(zone)
@@ -869,94 +975,38 @@ proc toAdjTime(dt: DateTime): Time =
   result = initTime(seconds, dt.nanosecond)
 
 when defined(JS):
-    type JsDate = object
-    proc newDate(year, month, date, hours, minutes, seconds, milliseconds: int): JsDate {.tags: [], raises: [], importc: "new Date".}
-    proc newDate(): JsDate {.importc: "new Date".}
-    proc newDate(value: float): JsDate {.importc: "new Date".}
-    proc getTimezoneOffset(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getTime(js: JsDate): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
-    proc getDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCDate(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCFullYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCHours(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMilliseconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMinutes(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCMonth(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCSeconds(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getUTCDay(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
-    proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
-
-    proc localZonedTimeFromTime(time: Time): ZonedTime =
-      let jsDate = newDate(time.seconds.float * 1000)
-      let offset = jsDate.getTimezoneOffset() * secondsInMin
-      result.time = time
-      result.utcOffset = offset
-      result.isDst = false
-
-    proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
-      let utcDate = newDate(adjTime.seconds.float * 1000)
-      let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
-        utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
-
-      # This is as dumb as it looks - JS doesn't support years in the range 0-99 in the constructor
-      # because they are assumed to be 19xx...
-      # Because JS doesn't support timezone history, it doesn't really matter in practice.
-      if utcDate.getUTCFullYear() in 0 .. 99:
-        localDate.setFullYear(utcDate.getUTCFullYear())
-
-      result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
-      result.time = adjTime + initDuration(seconds = result.utcOffset)
-      result.isDst = false
+  proc localZonedTimeFromTime(time: Time): ZonedTime =
+    let jsDate = newDate(time.seconds * 1000)
+    let offset = jsDate.getTimezoneOffset() * secondsInMin
+    result.time = time
+    result.utcOffset = offset
+    result.isDst = false
 
-else:
-  when defined(freebsd) or defined(netbsd) or defined(openbsd) or
-      defined(macosx):
-    type
-      StructTm {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        gmtoff {.importc: "tm_gmtoff".}: clong
-  else:
-    type
-      StructTm {.importc: "struct tm".} = object
-        second {.importc: "tm_sec".},
-          minute {.importc: "tm_min".},
-          hour {.importc: "tm_hour".},
-          monthday {.importc: "tm_mday".},
-          month {.importc: "tm_mon".},
-          year {.importc: "tm_year".},
-          weekday {.importc: "tm_wday".},
-          yearday {.importc: "tm_yday".},
-          isdst {.importc: "tm_isdst".}: cint
-        when defined(linux) and defined(amd64) or defined(haiku):
-          gmtoff {.importc: "tm_gmtoff".}: clong
-          zone {.importc: "tm_zone".}: cstring
-  type
-    StructTmPtr = ptr StructTm
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
+    let utcDate = newDate(adjTime.seconds * 1000)
+    let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(),
+        utcDate.getUTCDate(), utcDate.getUTCHours(), utcDate.getUTCMinutes(),
+        utcDate.getUTCSeconds(), 0)
+
+    # This is as dumb as it looks - JS doesn't support years in the range
+    # 0-99 in the constructor because they are assumed to be 19xx...
+    # Because JS doesn't support timezone history,
+    # it doesn't really matter in practice.
+    if utcDate.getUTCFullYear() in 0 .. 99:
+      localDate.setFullYear(utcDate.getUTCFullYear())
 
-  proc localtime(timer: ptr CTime): StructTmPtr {. importc: "localtime", header: "<time.h>", tags: [].}
+    result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
+    result.time = adjTime + initDuration(seconds = result.utcOffset)
+    result.isDst = false
 
-  proc toAdjUnix(tm: StructTm): int64 =
-    let epochDay = toEpochday(tm.monthday, (tm.month + 1).Month, tm.year.int + 1900)
+else:
+  proc toAdjUnix(tm: Tm): int64 =
+    let epochDay = toEpochday(tm.tm_mday, (tm.tm_mon + 1).Month,
+                              tm.tm_year.int + 1900)
     result = epochDay * secondsInDay
-    result.inc tm.hour * secondsInHour
-    result.inc tm.minute * 60
-    result.inc tm.second
+    result.inc tm.tm_hour * secondsInHour
+    result.inc tm.tm_min * 60
+    result.inc tm.tm_sec
 
   proc getLocalOffsetAndDst(unix: int64): tuple[offset: int, dst: bool] =
     # Windows can't handle unix < 0, so we fall back to unix = 0.
@@ -964,7 +1014,7 @@ else:
     when defined(windows):
       if unix < 0:
         var a = 0.CTime
-        let tmPtr = localtime(addr(a))
+        let tmPtr = localtime(a)
         if not tmPtr.isNil:
           let tm = tmPtr[]
           return ((0 - tm.toAdjUnix).int, false)
@@ -973,10 +1023,10 @@ else:
     # In case of a 32-bit time_t, we fallback to the closest available
     # timezone information.
     var a = clamp(unix, low(CTime), high(CTime)).CTime
-    let tmPtr = localtime(addr(a))
+    let tmPtr = localtime(a)
     if not tmPtr.isNil:
       let tm = tmPtr[]
-      return ((a.int64 - tm.toAdjUnix).int, tm.isdst > 0)
+      return ((a.int64 - tm.toAdjUnix).int, tm.tm_isdst > 0)
     return (0, false)
 
   proc localZonedTimeFromTime(time: Time): ZonedTime =
@@ -985,7 +1035,7 @@ else:
     result.utcOffset = offset
     result.isDst = dst
 
-  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime  =
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
     var adjUnix = adjTime.seconds
     let past = adjUnix - secondsInDay
     let (pastOffset, _) = getLocalOffsetAndDst(past)
@@ -995,7 +1045,7 @@ else:
 
     var utcOffset: int
     if pastOffset == futureOffset:
-        utcOffset = pastOffset.int
+      utcOffset = pastOffset.int
     else:
       if pastOffset > futureOffset:
         adjUnix -= secondsInHour
@@ -1029,8 +1079,8 @@ proc utc*(): TimeZone =
 proc local*(): TimeZone =
   ## Get the ``Timezone`` implementation for the local timezone.
   runnableExamples:
-   doAssert now().timezone == local()
-   doAssert local().name == "LOCAL"
+    doAssert now().timezone == local()
+    doAssert local().name == "LOCAL"
   if localInstance.isNil:
     localInstance = newTimezone("LOCAL", localZonedTimeFromTime,
       localZonedTimeFromAdjTime)
@@ -1064,7 +1114,8 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
   elif defined(macosx) or defined(freebsd):
     var a: Timeval
     gettimeofday(a)
-    result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int))
+    result = initTime(a.tv_sec.int64,
+                      convert(Microseconds, Nanoseconds, a.tv_usec.int))
   elif defined(posix):
     var ts: Timespec
     discard clock_gettime(realTimeClockId, ts)
@@ -1085,13 +1136,17 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds,
                        days, weeks, months, years: int = 0): TimeInterval =
   ## Creates a new ``TimeInterval``.
   ##
+  ## This proc doesn't perform any normalization! For example,
+  ## ``initTimeInterval(hours = 24)`` and ``initTimeInterval(days = 1)`` are
+  ## not equal.
+  ##
   ## You can also use the convenience procedures called ``milliseconds``,
   ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
-  ##
   runnableExamples:
-    let day = initTimeInterval(hours=24)
+    let day = initTimeInterval(hours = 24)
     let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc())
     doAssert $(dt + day) == "2000-01-02T12:00:00Z"
+    doAssert initTimeInterval(hours = 24) != initTimeInterval(days = 1)
   result.nanoseconds = nanoseconds
   result.microseconds = microseconds
   result.milliseconds = milliseconds
@@ -1119,7 +1174,7 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
 proc `-`*(ti: TimeInterval): TimeInterval =
   ## Reverses a time interval
   runnableExamples:
-    let day = -initTimeInterval(hours=24)
+    let day = -initTimeInterval(hours = 24)
     doAssert day.hours == -24
 
   result = TimeInterval(
@@ -1140,9 +1195,9 @@ proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ##
   ## Time components are subtracted one-by-one, see output:
   runnableExamples:
-    let ti1 = initTimeInterval(hours=24)
-    let ti2 = initTimeInterval(hours=4)
-    doAssert (ti1 - ti2) == initTimeInterval(hours=20)
+    let ti1 = initTimeInterval(hours = 24)
+    let ti2 = initTimeInterval(hours = 4)
+    doAssert (ti1 - ti2) == initTimeInterval(hours = 20)
 
   result = ti1 + (-ti2)
 
@@ -1159,13 +1214,13 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
     ':' & intToStr(dt.second, 2)
 
 proc toParts* (ti: TimeInterval): TimeIntervalParts =
-  ## Converts a `TimeInterval` into an array consisting of its time units,
-  ## starting with nanoseconds and ending with years
+  ## Converts a ``TimeInterval`` into an array consisting of its time units,
+  ## starting with nanoseconds and ending with years.
   ##
   ## This procedure is useful for converting ``TimeInterval`` values to strings.
   ## E.g. then you need to implement custom interval printing
   runnableExamples:
-    var tp = toParts(initTimeInterval(years=1, nanoseconds=123))
+    var tp = toParts(initTimeInterval(years = 1, nanoseconds = 123))
     doAssert tp[Years] == 1
     doAssert tp[Nanoseconds] == 123
 
@@ -1175,9 +1230,10 @@ proc toParts* (ti: TimeInterval): TimeIntervalParts =
     index += 1
 
 proc `$`*(ti: TimeInterval): string =
-  ## Get string representation of `TimeInterval`
+  ## Get string representation of ``TimeInterval``.
   runnableExamples:
-    doAssert $initTimeInterval(years=1, nanoseconds=123) == "1 year and 123 nanoseconds"
+    doAssert $initTimeInterval(years = 1, nanoseconds = 123) ==
+      "1 year and 123 nanoseconds"
     doAssert $initTimeInterval() == "0 nanoseconds"
 
   var parts: seq[string] = @[]
@@ -1203,7 +1259,7 @@ proc milliseconds*(ms: int): TimeInterval {.inline.} =
 proc seconds*(s: int): TimeInterval {.inline.} =
   ## TimeInterval of ``s`` seconds.
   ##
-  ## ``echo getTime() + 5.second``
+  ## ``echo getTime() + 5.seconds``
   initTimeInterval(seconds = s)
 
 proc minutes*(m: int): TimeInterval {.inline.} =
@@ -1242,7 +1298,8 @@ proc years*(y: int): TimeInterval {.inline.} =
   ## ``echo getTime() + 2.years``
   initTimeInterval(years = y)
 
-proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDur: Duration] =
+proc evaluateInterval(dt: DateTime, interval: TimeInterval):
+    tuple[adjDur, absDur: Duration] =
   ## Evaluates how many nanoseconds the interval is worth
   ## in the context of ``dt``.
   ## The result in split into an adjusted diff and an absolute diff.
@@ -1281,10 +1338,10 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDu
     minutes = interval.minutes,
     hours = interval.hours)
 
-
 proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
                    hour: HourRange, minute: MinuteRange, second: SecondRange,
-                   nanosecond: NanosecondRange, zone: Timezone = local()): DateTime =
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime =
   ## Create a new ``DateTime`` in the specified timezone.
   runnableExamples:
     let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc())
@@ -1292,12 +1349,12 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
 
   assertValidDate monthday, month, year
   let dt = DateTime(
-    monthday:  monthday,
-    year:  year,
-    month:  month,
-    hour:  hour,
-    minute:  minute,
-    second:  second,
+    monthday: monthday,
+    year: year,
+    month: month,
+    hour: hour,
+    minute: minute,
+    second: second,
     nanosecond: nanosecond
   )
   result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone)
@@ -1314,14 +1371,15 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
 
 proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
   ## Adds ``interval`` to ``dt``. Components from ``interval`` are added
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
-  ##
-  ## Note that when adding months, monthday overflow is allowed. This means that if the resulting
-  ## month doesn't have enough days it, the month will be incremented and the monthday will be
-  ## set to the number of days overflowed. So adding one month to `31 October` will result in `31 November`,
-  ## which will overflow and result in `1 December`.
+  ## in the order of their size, i.e first the ``years`` component, then the
+  ## ``months`` component and so on. The returned ``DateTime`` will have the
+  ## same timezone as the input.
   ##
+  ## Note that when adding months, monthday overflow is allowed. This means that
+  ## if the resulting month doesn't have enough days it, the month will be
+  ## incremented and the monthday will be set to the number of days overflowed.
+  ## So adding one month to `31 October` will result in `31 November`, which
+  ## will overflow and result in `1 December`.
   runnableExamples:
     let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
     doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z"
@@ -1341,9 +1399,10 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
     result = initDateTime(zt, dt.timezone)
 
 proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
-  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
-  ## in the order of their size, i.e first the ``years`` component, then the ``months``
-  ## component and so on. The returned ``DateTime`` will have the same timezone as the input.
+  ## Subtract ``interval`` from ``dt``. Components from ``interval`` are
+  ## subtracted in the order of their size, i.e first the ``years`` component,
+  ## then the ``months`` component and so on. The returned ``DateTime`` will
+  ## have the same timezone as the input.
   runnableExamples:
     let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc())
     doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z"
@@ -1377,15 +1436,15 @@ proc `-`*(dt1, dt2: DateTime): Duration =
   dt1.toTime - dt2.toTime
 
 proc `<`*(a, b: DateTime): bool =
-  ## Returns true iff ``a < b``, that is iff a happened before b.
+  ## Returns true iff ``a`` happened before ``b``.
   return a.toTime < b.toTime
 
-proc `<=` * (a, b: DateTime): bool =
-  ## Returns true iff ``a <= b``.
+proc `<=`*(a, b: DateTime): bool =
+  ## Returns true iff ``a`` happened before or at the same time as ``b``.
   return a.toTime <= b.toTime
 
 proc `==`*(a, b: DateTime): bool =
-  ## Returns true if ``a == b``, that is if both dates represent the same point in time.
+  ## Returns true iff ``a`` and ``b`` represent the same point in time.
   return a.toTime == b.toTime
 
 proc isStaticInterval(interval: TimeInterval): bool =
@@ -1486,8 +1545,8 @@ proc between*(startDt, endDt: DateTime): TimeInterval =
 
   #Months
   if endDt.month < startDt.month:
-      result.months = endDt.month.int() + 12 - startDt.month.int()
-      endDt.year -= 1
+    result.months = endDt.month.int() + 12 - startDt.month.int()
+    endDt.year -= 1
   else:
     result.months = endDt.month.int() -
       startDt.month.int()
@@ -1602,7 +1661,13 @@ type
       ## be encoded as ``@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]``.
     formatStr: string
 
-const FormatLiterals = { ' ', '-', '/', ':', '(', ')', '[', ']', ',' }
+  TimeParseError* = object of ValueError ## \
+    ## Raised when parsing input using a ``TimeFormat`` fails.
+
+  TimeFormatParseError* = object of ValueError ## \
+    ## Raised when parsing a ``TimeFormat`` string fails.
+
+const FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','}
 
 proc `$`*(f: TimeFormat): string =
   ## Returns the format string that was used to construct ``f``.
@@ -1612,9 +1677,34 @@ proc `$`*(f: TimeFormat): string =
   f.formatStr
 
 proc raiseParseException(f: TimeFormat, input: string, msg: string) =
-  raise newException(ValueError,
+  raise newException(TimeParseError,
                      &"Failed to parse '{input}' with format '{f}'. {msg}")
 
+proc parseInt(s: string, b: var int, start = 0, maxLen = int.high,
+              allowSign = false): int =
+  var sign = -1
+  var i = start
+  let stop = start + min(s.high - start + 1, maxLen) - 1
+  if allowSign and i <= stop:
+    if s[i] == '+':
+      inc(i)
+    elif s[i] == '-':
+      inc(i)
+      sign = 1
+  if i <= stop and s[i] in {'0'..'9'}:
+    b = 0
+    while i <= stop and s[i] in {'0'..'9'}:
+      let c = ord(s[i]) - ord('0')
+      if b >= (low(int) + c) div 10:
+        b = b * 10 - c
+      else:
+        return 0
+      inc(i)
+    if sign == -1 and b == low(int):
+      return 0
+    b = b * sign
+    result = i - start
+
 iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] =
   var i = 0
   var currToken = ""
@@ -1639,15 +1729,15 @@ iterator tokens(f: string): tuple[kind: FormatTokenKind, token: string] =
           i.inc
 
         if i > f.high:
-          raise newException(ValueError,
+          raise newException(TimeFormatParseError,
                              &"Unclosed ' in time format string. " &
                              "For a literal ', use ''.")
         i.inc
         yield (tkLiteral, token)
     of FormatLiterals:
-        yieldCurrToken()
-        yield (tkLiteral, $f[i])
-        i.inc
+      yieldCurrToken()
+      yield (tkLiteral, $f[i])
+      i.inc
     else:
       # Check if the letter being added matches previous accumulated buffer.
       if currToken.len == 0 or currToken[0] == f[i]:
@@ -1696,7 +1786,8 @@ proc stringToPattern(str: string): FormatPattern =
   of "zzz": result = zzz
   of "zzzz": result = zzzz
   of "g": result = g
-  else: raise newException(ValueError, &"'{str}' is not a valid pattern")
+  else: raise newException(TimeFormatParseError,
+                           &"'{str}' is not a valid pattern")
 
 proc initTimeFormat*(format: string): TimeFormat =
   ## Construct a new time format for parsing & formatting time types.
@@ -1715,7 +1806,7 @@ proc initTimeFormat*(format: string): TimeFormat =
       else:
         result.patterns.add(FormatPattern.Lit.byte)
         if token.len > 255:
-          raise newException(ValueError,
+          raise newException(TimeFormatParseError,
                              "Format literal is to long:" & token)
         result.patterns.add(token.len.byte)
         for c in token:
@@ -1804,12 +1895,12 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) =
     else:
       result.add '+' & $year
   of UUUU:
-      result.add $dt.year
+    result.add $dt.year
   of z, zz, zzz, zzzz:
     if dt.timezone != nil and dt.timezone.name == "Etc/UTC":
       result.add 'Z'
     else:
-      result.add  if -dt.utcOffset >= 0: '+' else: '-'
+      result.add if -dt.utcOffset >= 0: '+' else: '-'
       let absOffset = abs(dt.utcOffset)
       case pattern:
       of z:
@@ -1828,20 +1919,15 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string) =
         result.add h & ":" & m & ":" & s
       else: assert false
   of g:
-      result.add if dt.year < 1: "BC" else: "AD"
+    result.add if dt.year < 1: "BC" else: "AD"
   of Lit: assert false # Can't happen
 
 proc parsePattern(input: string, pattern: FormatPattern, i: var int,
                   parsed: var ParsedTime): bool =
-  template takeInt(allowedWidth: Slice[int]): int =
+  template takeInt(allowedWidth: Slice[int], allowSign = false): int =
     var sv: int
-    let max = i + allowedWidth.b - 1
-    var pd =
-      if max > input.high:
-        parseInt(input, sv, i)
-      else:
-        parseInt(input[i..max], sv)
-    if pd notin allowedWidth:
+    var pd = parseInt(input, sv, i, allowedWidth.b, allowSign)
+    if pd < allowedWidth.a:
       return false
     i.inc pd
     sv
@@ -1853,11 +1939,13 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
 
   case pattern
   of d:
-    parsed.monthday = some(takeInt(1..2))
-    result = parsed.monthday.get() in MonthdayRange
+    let monthday = takeInt(1..2)
+    parsed.monthday = some(monthday)
+    result = monthday in MonthdayRange
   of dd:
-    parsed.monthday = some(takeInt(2..2))
-    result = parsed.monthday.get() in MonthdayRange
+    let monthday = takeInt(2..2)
+    parsed.monthday = some(monthday)
+    result = monthday in MonthdayRange
   of ddd:
     result = input.substr(i, i+2).toLowerAscii() in [
       "sun", "mon", "tue", "wed", "thu", "fri", "sat"]
@@ -1992,8 +2080,8 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     result = year > 0
   of yyyy:
     let year =
-      if input[i] in { '+', '-' }:
-        takeInt(4..high(int))
+      if input[i] in {'+', '-'}:
+        takeInt(4..high(int), allowSign = true)
       else:
         takeInt(4..4)
     result = year > 0
@@ -2004,13 +2092,13 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     result = year > 0
   of uuuu:
     let year =
-      if input[i] in { '+', '-' }:
-        takeInt(4..high(int))
+      if input[i] in {'+', '-'}:
+        takeInt(4..high(int), allowSign = true)
       else:
         takeInt(4..4)
     parsed.year = some(year)
   of UUUU:
-    parsed.year = some(takeInt(1..high(int)))
+    parsed.year = some(takeInt(1..high(int), allowSign = true))
   of z, zz, zzz, zzzz:
     case input[i]
     of '+', '-':
@@ -2055,9 +2143,8 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     else:
       result = false
   of y, yyy, yyyyy:
-    raise newException(ValueError,
-                      &"The pattern '{pattern}' is only valid for formatting")
-  of Lit: assert false # Can't happen
+    raiseAssert "Pattern is invalid for parsing: " & $pattern
+  of Lit: doAssert false, "Can't happen"
 
 proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
                 input: string): DateTime =
@@ -2147,7 +2234,8 @@ proc format*(dt: DateTime, f: TimeFormat): string {.raises: [].} =
       formatPattern(dt, f.patterns[idx].FormatPattern, result = result)
       idx.inc
 
-proc format*(dt: DateTime, f: string): string =
+proc format*(dt: DateTime, f: string): string
+    {.raises: [TimeFormatParseError].} =
   ## Shorthand for constructing a ``TimeFormat`` and using it to format ``dt``.
   ##
   ## See `Parsing and formatting dates`_ for documentation of the
@@ -2163,7 +2251,8 @@ proc format*(dt: DateTime, f: static[string]): string {.raises: [].} =
   const f2 = initTimeFormat(f)
   result = dt.format(f2)
 
-proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} =
+proc format*(time: Time, f: string, zone: Timezone = local()): string
+    {.raises: [TimeFormatParseError].} =
   ## Shorthand for constructing a ``TimeFormat`` and using it to format
   ## ``time``. Will use the timezone specified by ``zone``.
   ##
@@ -2175,13 +2264,14 @@ proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: []
     doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00"
   time.inZone(zone).format(f)
 
-proc format*(time: Time, f: static[string],
-             zone: Timezone = local()): string {.tags: [].} =
+proc format*(time: Time, f: static[string], zone: Timezone = local()): string
+    {.raises: [].} =
   ## Overload that validates ``f`` at compile time.
   const f2 = initTimeFormat(f)
   result = time.inZone(zone).format(f2)
 
-proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime =
+proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime
+    {.raises: [TimeParseError, Defect].} =
   ## Parses ``input`` as a ``DateTime`` using the format specified by ``f``.
   ## If no UTC offset was parsed, then ``input`` is assumed to be specified in
   ## the ``zone`` timezone. If a UTC offset was parsed, the result will be
@@ -2217,11 +2307,12 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local()): DateTime =
 
   if patIdx <= f.patterns.high:
     raiseParseException(f, input,
-                        "Parsing ended but there was still patterns remaining")
+                            "Parsing ended but there was still patterns remaining")
 
   result = toDateTime(parsed, zone, f, input)
 
-proc parse*(input, f: string, tz: Timezone = local()): DateTime =
+proc parse*(input, f: string, tz: Timezone = local()): DateTime
+    {.raises: [TimeParseError, TimeFormatParseError, Defect].} =
   ## Shorthand for constructing a ``TimeFormat`` and using it to parse
   ## ``input`` as a ``DateTime``.
   ##
@@ -2233,12 +2324,14 @@ proc parse*(input, f: string, tz: Timezone = local()): DateTime =
   let dtFormat = initTimeFormat(f)
   result = input.parse(dtFormat, tz)
 
-proc parse*(input: string, f: static[string], zone: Timezone = local()): DateTime =
+proc parse*(input: string, f: static[string], zone: Timezone = local()):
+    DateTime {.raises: [TimeParseError, Defect].} =
   ## Overload that validates ``f`` at compile time.
   const f2 = initTimeFormat(f)
   result = input.parse(f2, zone)
 
-proc parseTime*(input, f: string, zone: Timezone): Time =
+proc parseTime*(input, f: string, zone: Timezone): Time
+    {.raises: [TimeParseError, TimeFormatParseError, Defect].} =
   ## Shorthand for constructing a ``TimeFormat`` and using it to parse
   ## ``input`` as a ``DateTime``, then converting it a ``Time``.
   ##
@@ -2249,7 +2342,8 @@ proc parseTime*(input, f: string, zone: Timezone): Time =
     doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", utc()) == fromUnix(0)
   parse(input, f, zone).toTime()
 
-proc parseTime*(input: string, f: static[string], zone: Timezone): Time =
+proc parseTime*(input: string, f: static[string], zone: Timezone): Time
+    {.raises: [TimeParseError, Defect].} =
   ## Overload that validates ``format`` at compile time.
   const f2 = initTimeFormat(f)
   result = input.parse(f2, zone).toTime()
@@ -2267,7 +2361,7 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
   result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz")
 
 proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
-  ## converts a `Time` value to a string representation. It will use the local
+  ## Converts a `Time` value to a string representation. It will use the local
   ## time zone and use the format ``yyyy-MM-dd'T'HH-mm-sszzz``.
   runnableExamples:
     let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
@@ -2275,8 +2369,6 @@ proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
     doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz")
   $time.local
 
-{.pop.}
-
 proc countLeapYears*(yearSpan: int): int =
   ## Returns the number of leap years spanned by a given number of years.
   ##
@@ -2318,7 +2410,8 @@ when not defined(JS):
   type
     Clock {.importc: "clock_t".} = distinct int
 
-  proc getClock(): Clock {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
+  proc getClock(): Clock
+      {.importc: "clock", header: "<time.h>", tags: [TimeEffect].}
 
   var
     clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int
@@ -2376,59 +2469,68 @@ when defined(JS):
 # Deprecated procs
 
 when not defined(JS):
-  proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} =
+  proc unixTimeToWinTime*(time: CTime): int64
+      {.deprecated: "Use toWinTime instead".} =
     ## Converts a UNIX `Time` (``time_t``) to a Windows file time
     ##
     ## **Deprecated:** use ``toWinTime`` instead.
     result = int64(time) * rateDiff + epochDiff
 
-  proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} =
+  proc winTimeToUnixTime*(time: int64): CTime
+      {.deprecated: "Use fromWinTime instead".} =
     ## Converts a Windows time to a UNIX `Time` (``time_t``)
     ##
     ## **Deprecated:** use ``fromWinTime`` instead.
     result = CTime((time - epochDiff) div rateDiff)
 
-proc initInterval*(seconds, minutes, hours, days, months,
-                   years: int = 0): TimeInterval {.deprecated.} =
+proc initInterval*(seconds, minutes, hours, days, months, years: int = 0):
+    TimeInterval {.deprecated.} =
   ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead.
   initTimeInterval(0, 0, 0, seconds, minutes, hours, days, 0, months, years)
 
-proc fromSeconds*(since1970: float): Time {.tags: [], raises: [], benign, deprecated.} =
+proc fromSeconds*(since1970: float): Time
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Takes a float which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
-  let nanos = ((since1970 - since1970.int64.float) * convert(Seconds, Nanoseconds, 1).float).int
+  let nanos = ((since1970 - since1970.int64.float) *
+    convert(Seconds, Nanoseconds, 1).float).int
   initTime(since1970.int64, nanos)
 
-proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprecated.} =
+proc fromSeconds*(since1970: int64): Time
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Takes an int which contains the number of seconds since the unix epoch and
   ## returns a time object.
   ##
   ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
   fromUnix(since1970)
 
-proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
+proc toSeconds*(time: Time): float
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Returns the time in seconds since the unix epoch.
   ##
   ## **Deprecated since v0.18.0:** use ``toUnix`` instead
   time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
 
-proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+proc getLocalTime*(time: Time): DateTime
+    {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-time representation,
   ## expressed relative to the user's specified time zone.
   ##
   ## **Deprecated since v0.18.0:** use ``local`` instead
   time.local
 
-proc getGMTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
+proc getGMTime*(time: Time): DateTime
+      {.tags: [], raises: [], benign, deprecated.} =
   ## Converts the calendar time `time` to broken-down time representation,
   ## expressed in Coordinated Universal Time (UTC).
   ##
   ## **Deprecated since v0.18.0:** use ``utc`` instead
   time.utc
 
-proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.} =
+proc getTimezone*(): int
+    {.tags: [TimeEffect], raises: [], benign, deprecated.} =
   ## Returns the offset of the local (non-DST) timezone in seconds west of UTC.
   ##
   ## **Deprecated since v0.18.0:** use ``now().utcOffset`` to get the current
@@ -2436,45 +2538,14 @@ proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign, deprecated.}
   when defined(JS):
     return newDate().getTimezoneOffset() * 60
   elif defined(freebsd) or defined(netbsd) or defined(openbsd):
-    var a: CTime
-    discard time(a)
-    let lt = localtime(addr(a))
-    # BSD stores in `gmtoff` offset east of UTC in seconds,
-    # but posix systems using west of UTC in seconds
-    return -(lt.gmtoff)
+    # This is wrong since it will include DST offsets, but the behavior has
+    # always been wrong for bsd and the proc is deprecated so lets ignore it.
+    return now().utcOffset
   else:
     return timezone
 
-proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
-  ## Converts a broken-down time structure to calendar time representation.
-  ##
-  ## **Deprecated since v0.14.0:** use ``toTime`` instead.
-  dt.toTime
-
-when defined(JS):
-  var start = getTime()
-  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    let dur = getTime() - start
-    result = (convert(Seconds, Milliseconds, dur.seconds) +
-      convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
-else:
-  proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    ## get the milliseconds from the start of the program.
-    ##
-    ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
-    when defined(macosx):
-      result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
-    else:
-      result = int(getClock()) div (clocksPerSec div 1000)
-
-proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
-  ## Converts a Time to a TimeInterval.
-  ##
-  ## **Deprecated since v0.14.0:** use ``toTimeInterval`` instead.
-  # Milliseconds not available from Time
-  t.toTimeInterval()
-
-proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
+proc getDayOfWeek*(day, month, year: int): WeekDay
+    {.tags: [], raises: [], benign, deprecated.} =
   ## **Deprecated since v0.18.0:** use
   ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
   getDayOfWeek(day, month.Month, year)
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index c53b68864..a373a9370 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -10,30 +10,13 @@
 ## This module defines compile-time reflection procs for
 ## working with types
 
-proc name*(t: typedesc): string {.magic: "TypeTrait".}
-  ## Returns the name of the given type.
-  ##
-  ## Example:
-  ##
-  ## .. code-block::
-  ##
-  ##   import typetraits
-  ##
-  ##   proc `$`*(T: typedesc): string = name(T)
-  ##
-  ##   template test(x): typed =
-  ##     echo "type: ", type(x), ", value: ", x
-  ##
-  ##   test 42
-  ##   # --> type: int, value: 42
-  ##   test "Foo"
-  ##   # --> type: string, value: Foo
-  ##   test(@['A','B'])
-  ##   # --> type: seq[char], value: @[A, B]
+include "system/helpers" # for `isNamedTuple`
+
+export system.`$`
+export isNamedTuple
 
-proc `$`*(t: typedesc): string =
-  ## An alias for `name`.
-  name(t)
+proc name*(t: typedesc): string {.magic: "TypeTrait".}
+  ## Alias for system.`$`(t) since Nim v0.20.0.
 
 proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
   ## Returns the arity of the given type. This is the number of "type" components or
@@ -64,4 +47,22 @@ proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
 
 
 when isMainModule:
-  doAssert $type(42) == "int"
+  static:
+    doAssert $type(42) == "int"
+    doAssert int.name == "int"
+
+  const a1 = name(int)
+  const a2 = $(int)
+  const a3 = $int
+  doAssert a1 == "int"
+  doAssert a2 == "int"
+  doAssert a3 == "int"
+
+  proc fun[T: typedesc](t: T) =
+    const a1 = name(t)
+    const a2 = $(t)
+    const a3 = $t
+    doAssert a1 == "int"
+    doAssert a2 == "int"
+    doAssert a3 == "int"
+  fun(int)
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 712cc46c8..27eabecc6 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -8,6 +8,11 @@
 #
 
 ## This module provides support to handle the Unicode UTF-8 encoding.
+##
+## There are no specialized ``insert``, ``delete``, ``add`` and ``contains``
+## procedures for ``seq[Rune]`` in this module because the generic variants
+## of these procedures in the system module already work with it.
+
 
 {.deadCodeElim: on.}  # dce option deprecated
 
@@ -15,7 +20,7 @@ include "system/inclrtl"
 
 type
   RuneImpl = int32 # underlying type of Rune
-  Rune* = distinct RuneImpl   ## type that can hold any Unicode character
+  Rune* = distinct RuneImpl ## Unicode code point. Can hold any Unicode character.
   Rune16* = distinct int16 ## 16 bit Unicode character
 
 proc `<=%`*(a, b: Rune): bool = return int(a) <=% int(b)
@@ -25,7 +30,12 @@ proc `==`*(a, b: Rune): bool = return int(a) == int(b)
 template ones(n: untyped): untyped = ((1 shl n)-1)
 
 proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
-  ## Returns the number of Unicode characters of the string ``s``
+  ## Returns the number of runes of the string ``s``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLen == 6
+    ## note: a.len == 8
+
   var i = 0
   while i < len(s):
     if ord(s[i]) <=% 127: inc(i)
@@ -38,7 +48,12 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
     inc(result)
 
 proc runeLenAt*(s: string, i: Natural): int =
-  ## Returns the number of bytes the rune starting at ``s[i]`` takes
+  ## Returns the number of bytes the rune starting at ``s[i]`` takes.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLenAt(0) == 1
+    doAssert a.runeLenAt(1) == 2
+
   if ord(s[i]) <=% 127: result = 1
   elif ord(s[i]) shr 5 == 0b110: result = 2
   elif ord(s[i]) shr 4 == 0b1110: result = 3
@@ -50,7 +65,7 @@ proc runeLenAt*(s: string, i: Natural): int =
 const replRune = Rune(0xFFFD)
 
 template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) =
-  ## Returns the Unicode character ``s[i]`` in ``result``. If ``doInc == true``
+  ## Returns the rune ``s[i]`` in ``result``. If ``doInc == true``
   ## ``i`` is incremented by the number of bytes that have been processed.
   bind ones
   if ord(s[i]) <=% 127:
@@ -152,17 +167,21 @@ proc validateUtf8*(s: string): int =
   return -1
 
 proc runeAt*(s: string, i: Natural): Rune =
-  ## Returns the unicode character in ``s`` at byte index ``i``
+  ## Returns the rune in ``s`` at **byte index** ``i``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeAt(1) == "ñ".runeAt(0)
+    doAssert a.runeAt(2) == "ñ".runeAt(1)
+    doAssert a.runeAt(3) == "y".runeAt(0)
   fastRuneAt(s, i, result, false)
 
 template fastToUTF8Copy*(c: Rune, s: var string, pos: int, doInc = true) =
-  ## Copies UTF-8 representation of `c` into the preallocated string `s`
-  ## starting at position `pos`. If `doInc == true`, `pos` is incremented
+  ## Copies UTF-8 representation of ``c`` into the preallocated string ``s``
+  ## starting at position ``pos``. If ``doInc == true``, ``pos`` is incremented
   ## by the number of bytes that have been processed.
   ##
-  ## To be the most efficient, make sure `s` is preallocated
-  ## with an additional amount equal to the byte length of
-  ## `c`.
+  ## To be the most efficient, make sure ``s`` is preallocated
+  ## with an additional amount equal to the byte length of ``c``.
   var i = RuneImpl(c)
   if i <=% 127:
     s.setLen(pos+1)
@@ -207,28 +226,39 @@ template fastToUTF8Copy*(c: Rune, s: var string, pos: int, doInc = true) =
     discard # error, exception?
 
 proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
-  ## Converts a rune into its UTF-8 representation
+  ## Converts a rune into its UTF-8 representation.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeAt(1).toUTF8 == "ñ"
+
   result = ""
   fastToUTF8Copy(c, result, 0, false)
 
 proc add*(s: var string; c: Rune) =
+  ## Adds a rune ``c`` to a string ``s``.
+  runnableExamples:
+    var s = "abc"
+    let c = "ä".runeAt(0)
+    s.add(c)
+    doAssert s == "abcä"
+
   let pos = s.len
   fastToUTF8Copy(c, s, pos, false)
 
 proc `$`*(rune: Rune): string =
-  ## Converts a Rune to a string
+  ## An alias for `toUTF8 <#toUTF8%2CRune>`_.
   rune.toUTF8
 
 proc `$`*(runes: seq[Rune]): string =
-  ## Converts a sequence of Runes to a string
+  ## Converts a sequence of Runes to a string.
   result = ""
   for rune in runes:
     result.add rune
 
 proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int =
-  ## Returns the byte position of unicode character
-  ## at position pos in s with an optional start byte position.
-  ## returns the special value -1 if it runs out of the string
+  ## Returns the byte position of rune
+  ## at position ``pos`` in ``s`` with an optional start byte position.
+  ## Returns the special value -1 if it runs out of the string.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -244,7 +274,7 @@ proc runeOffset*(s: string, pos:Natural, start: Natural = 0): int =
   return o
 
 proc runeAtPos*(s: string, pos: int): Rune =
-  ## Returns the unicode character at position pos
+  ## Returns the rune at position ``pos``.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -252,7 +282,7 @@ proc runeAtPos*(s: string, pos: int): Rune =
   fastRuneAt(s, runeOffset(s, pos), result, false)
 
 proc runeStrAtPos*(s: string, pos: Natural): string =
-  ## Returns the unicode character at position pos as UTF8 String
+  ## Returns the rune at position ``pos`` as UTF8 String.
   ##
   ## Beware: This can lead to unoptimized code and slow execution!
   ## Most problems can be solved more efficiently by using an iterator
@@ -262,7 +292,7 @@ proc runeStrAtPos*(s: string, pos: Natural): string =
 
 proc runeReverseOffset*(s: string, rev:Positive): (int, int) =
   ## Returns a tuple with the the byte offset of the
-  ## unicode character at position ``rev`` in s counting
+  ## rune at position ``rev`` in ``s``, counting
   ## from the end (starting with 1) and the total
   ## number of runes in the string. Returns a negative value
   ## for offset if there are to few runes in the string to
@@ -286,13 +316,21 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) =
     return (-a, rev.int-a)
   return (x, -a+rev.int)
 
-proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
-  ## Returns the UTF-8 substring starting at codepoint pos
-  ## with len codepoints. If pos or len is negative they count from
-  ## the end of the string. If len is not given it means the longest
+proc runeSubStr*(s: string, pos: int, len: int = int.high): string =
+  ## Returns the UTF-8 substring starting at codepoint ``pos``
+  ## with ``len`` codepoints. If ``pos`` or ``len`` is negative they count from
+  ## the end of the string. If ``len`` is not given it means the longest
   ## possible string.
   ##
-  ## (Needs some examples)
+  runnableExamples:
+    let s = "Hänsel  ««: 10,00€"
+    doAssert(runeSubStr(s, 0, 2) == "Hä")
+    doAssert(runeSubStr(s, 10, 1) == ":")
+    doAssert(runeSubStr(s, -6) == "10,00€")
+    doAssert(runeSubStr(s, 10) == ": 10,00€")
+    doAssert(runeSubStr(s, 12, 5) == "10,00")
+    doAssert(runeSubStr(s, -6, 3) == "10,")
+
   if pos < 0:
     let (o, rl) = runeReverseOffset(s, -pos)
     if len >= rl:
@@ -1321,7 +1359,7 @@ proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
   return -1
 
 proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into lower case. This works for any Unicode character.
+  ## Converts ``c`` into lower case. This works for any rune.
   ## If possible, prefer ``toLower`` over ``toUpper``.
   var c = RuneImpl(c)
   var p = binarySearch(c, tolowerRanges, len(tolowerRanges) div 3, 3)
@@ -1333,7 +1371,7 @@ proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` into upper case. This works for any Unicode character.
+  ## Converts ``c`` into upper case. This works for any rune.
   ## If possible, prefer ``toLower`` over ``toUpper``.
   var c = RuneImpl(c)
   var p = binarySearch(c, toupperRanges, len(toupperRanges) div 3, 3)
@@ -1345,7 +1383,7 @@ proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
-  ## Converts ``c`` to title case
+  ## Converts ``c`` to title case.
   var c = RuneImpl(c)
   var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
   if p >= 0 and c == toTitleSinglets[p]:
@@ -1353,7 +1391,7 @@ proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} =
   return Rune(c)
 
 proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a lower case Unicode character.
+  ## Returns true iff ``c`` is a lower case rune.
   ## If possible, prefer ``isLower`` over ``isUpper``.
   var c = RuneImpl(c)
   # Note: toUpperRanges is correct here!
@@ -1365,7 +1403,7 @@ proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a upper case Unicode character.
+  ## Returns true iff ``c`` is a upper case rune.
   ## If possible, prefer ``isLower`` over ``isUpper``.
   var c = RuneImpl(c)
   # Note: toLowerRanges is correct here!
@@ -1377,7 +1415,7 @@ proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is an *alpha* Unicode character (i.e., a letter)
+  ## Returns true iff ``c`` is an *alpha* rune (i.e., a letter)
   if isUpper(c) or isLower(c):
     return true
   var c = RuneImpl(c)
@@ -1389,18 +1427,18 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
     return true
 
 proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode titlecase character
+  ## Returns true iff ``c`` is a Unicode titlecase character.
   return isUpper(c) and isLower(c)
 
 proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode whitespace character
+  ## Returns true iff ``c`` is a Unicode whitespace character.
   var c = RuneImpl(c)
   var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
   if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]:
     return true
 
 proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} =
-  ## Returns true iff ``c`` is a Unicode combining character
+  ## Returns true iff ``c`` is a Unicode combining character.
   var c = RuneImpl(c)
 
   # Optimized to return false immediately for ASCII
@@ -1424,12 +1462,12 @@ template runeCheck(s, runeProc) =
 
 proc isAlpha*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all alphabetic unicode characters.
+  ## Returns true iff ``s`` contains all alphabetic runes.
   runeCheck(s, isAlpha)
 
 proc isSpace*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Returns true iff `s` contains all whitespace unicode characters.
+  ## Returns true iff ``s`` contains all whitespace runes.
   runeCheck(s, isWhiteSpace)
 
 template runeCaseCheck(s, runeProc, skipNonAlpha) =
@@ -1459,7 +1497,7 @@ proc isLower*(s: string, skipNonAlpha: bool): bool {.
   ## Checks whether ``s`` is lower case.
   ##
   ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## runes in ``s`` are lower case.  Returns false if none of the
+  ## runes in ``s`` are lower case. Returns false if none of the
   ## runes in ``s`` are alphabetical.
   ##
   ## If ``skipNonAlpha`` is false, returns true only if all runes in
@@ -1474,7 +1512,7 @@ proc isUpper*(s: string, skipNonAlpha: bool): bool {.
   ## Checks whether ``s`` is upper case.
   ##
   ## If ``skipNonAlpha`` is true, returns true if all alphabetical
-  ## runes in ``s`` are upper case.  Returns false if none of the
+  ## runes in ``s`` are upper case. Returns false if none of the
   ## runes in ``s`` are alphabetical.
   ##
   ## If ``skipNonAlpha`` is false, returns true only if all runes in
@@ -1485,7 +1523,7 @@ proc isUpper*(s: string, skipNonAlpha: bool): bool {.
   runeCaseCheck(s, isUpper, skipNonAlpha)
 
 template convertRune(s, runeProc) =
-  ## Convert runes in `s` using `runeProc` as the converter.
+  ## Convert runes in ``s`` using ``runeProc`` as the converter.
   result = newString(len(s))
 
   var
@@ -1502,20 +1540,20 @@ template convertRune(s, runeProc) =
 
 proc toUpper*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into upper-case unicode characters.
+  ## Converts ``s`` into upper-case runes.
   convertRune(s, toUpper)
 
 proc toLower*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str".} =
-  ## Converts `s` into lower-case unicode characters.
+  ## Converts ``s`` into lower-case runes.
   convertRune(s, toLower)
 
 proc swapCase*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Swaps the case of unicode characters in `s`
+  ## Swaps the case of runes in ``s``.
   ##
-  ## Returns a new string such that the cases of all unicode characters
-  ## are swapped if possible
+  ## Returns a new string such that the cases of all runes
+  ## are swapped if possible.
 
   var
     i = 0
@@ -1538,7 +1576,7 @@ proc swapCase*(s: string): string {.noSideEffect, procvar,
 
 proc capitalize*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Converts the first character of `s` into an upper-case unicode character.
+  ## Converts the first character of ``s`` into an upper-case rune.
   if len(s) == 0:
     return s
 
@@ -1552,10 +1590,10 @@ proc capitalize*(s: string): string {.noSideEffect, procvar,
 
 proc translate*(s: string, replacements: proc(key: string): string): string {.
   rtl, extern: "nuc$1".} =
-  ## Translates words in a string using the `replacements` proc to substitute
-  ## words inside `s` with their replacements
+  ## Translates words in a string using the ``replacements`` proc to substitute
+  ## words inside ``s`` with their replacements.
   ##
-  ## `replacements` is any proc that takes a word and returns
+  ## ``replacements`` is any proc that takes a word and returns
   ## a new word to fill it's place.
 
   # Allocate memory for the new string based on the old one.
@@ -1601,10 +1639,10 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
 proc title*(s: string): string {.noSideEffect, procvar,
   rtl, extern: "nuc$1".} =
-  ## Converts `s` to a unicode title.
+  ## Converts ``s`` to a unicode title.
   ##
   ## Returns a new string such that the first character
-  ## in each word inside `s` is capitalized
+  ## in each word inside ``s`` is capitalized.
 
   var
     i = 0
@@ -1631,10 +1669,10 @@ proc title*(s: string): string {.noSideEffect, procvar,
 proc isTitle*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nuc$1Str",
   deprecated: "Deprecated since version 0.20 since its semantics are unclear".}=
-  ## Checks whether or not `s` is a unicode title.
+  ## Checks whether or not ``s`` is a unicode title.
   ##
-  ## Returns true if the first character in each word inside `s`
-  ## are upper case and there is at least one character in `s`.
+  ## Returns true if the first character in each word inside ``s``
+  ## are upper case and there is at least one character in ``s``.
   if s.len == 0:
     return false
 
@@ -1656,7 +1694,7 @@ proc isTitle*(s: string): bool {.noSideEffect, procvar,
       firstRune = true
 
 iterator runes*(s: string): Rune =
-  ## Iterates over any unicode character of the string ``s`` returning runes
+  ## Iterates over any rune of the string ``s`` returning runes.
   var
     i = 0
     result: Rune
@@ -1665,7 +1703,7 @@ iterator runes*(s: string): Rune =
     yield result
 
 iterator utf8*(s: string): string =
-  ## Iterates over any unicode character of the string ``s`` returning utf8 values
+  ## Iterates over any rune of the string ``s`` returning utf8 values.
   var o = 0
   while o < s.len:
     let n = runeLenAt(s, o)
@@ -1673,7 +1711,7 @@ iterator utf8*(s: string): string =
     o += n
 
 proc toRunes*(s: string): seq[Rune] =
-  ## Obtains a sequence containing the Runes in ``s``
+  ## Obtains a sequence containing the Runes in ``s``.
   result = newSeq[Rune]()
   for r in s.runes:
     result.add(r)
@@ -1696,15 +1734,14 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} =
   result = a.len - b.len
 
 proc reversed*(s: string): string =
-  ## Returns the reverse of ``s``, interpreting it as Unicode characters.
-  ## Unicode combining characters are correctly interpreted as well:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##   assert reversed("Reverse this!") == "!siht esreveR"
-  ##   assert reversed("先秦兩漢") == "漢兩秦先"
-  ##   assert reversed("as⃝df̅") == "f̅ds⃝a"
-  ##   assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+  ## Returns the reverse of ``s``, interpreting it as runes.
+  ## Unicode combining characters are correctly interpreted as well.
+  runnableExamples:
+    assert reversed("Reverse this!") == "!siht esreveR"
+    assert reversed("先秦兩漢") == "漢兩秦先"
+    assert reversed("as⃝df̅") == "f̅ds⃝a"
+    assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+
   var
     i = 0
     lastI = 0
@@ -1731,7 +1768,7 @@ proc reversed*(s: string): string =
   reverseUntil(len(s))
 
 proc graphemeLen*(s: string; i: Natural): Natural =
-  ## The number of bytes belonging to 's[i]' including following combining
+  ## The number of bytes belonging to ``s[i]`` including following combining
   ## characters.
   var j = i.int
   var r, r2: Rune
@@ -1744,7 +1781,7 @@ proc graphemeLen*(s: string; i: Natural): Natural =
       result = j-i
 
 proc lastRune*(s: string; last: int): (Rune, int) =
-  ## length of the last rune in 's[0..last]'. Returns the rune and its length
+  ## Length of the last rune in ``s[0..last]``. Returns the rune and its length
   ## in bytes.
   if s[last] <= chr(127):
     result = (Rune(s[last]), 1)
@@ -1778,7 +1815,7 @@ proc stringHasSep(s: string, index: int, sep: Rune): bool =
   return sep == rune
 
 template splitCommon(s, sep, maxsplit: untyped, sepLen: int = -1) =
-  ## Common code for split procedures
+  ## Common code for split procedures.
   var
     last = 0
     splits = maxsplit
@@ -1801,9 +1838,9 @@ template splitCommon(s, sep, maxsplit: untyped, sepLen: int = -1) =
 
 iterator split*(s: string, seps: openarray[Rune] = unicodeSpaces,
   maxsplit: int = -1): string =
-  ## Splits the unicode string `s` into substrings using a group of separators.
+  ## Splits the unicode string ``s`` into substrings using a group of separators.
   ##
-  ## Substrings are separated by a substring containing only `seps`.
+  ## Substrings are separated by a substring containing only ``seps``.
   ##
   ## .. code-block:: nim
   ##   for word in split("this\lis an\texample"):
@@ -1844,7 +1881,7 @@ iterator split*(s: string, seps: openarray[Rune] = unicodeSpaces,
   splitCommon(s, seps, maxsplit)
 
 iterator splitWhitespace*(s: string): string =
-  ## Splits a unicode string at whitespace runes
+  ## Splits a unicode string at whitespace runes.
   splitCommon(s, unicodeSpaces, -1)
 
 template accResult(iter: untyped) =
@@ -1858,9 +1895,9 @@ proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
   accResult(splitWhitespace(s))
 
 iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
-  ## Splits the unicode string `s` into substrings using a single separator.
+  ## Splits the unicode string ``s`` into substrings using a single separator.
   ##
-  ## Substrings are separated by the rune `sep`.
+  ## Substrings are separated by the rune ``sep``.
   ## The code:
   ##
   ## .. code-block:: nim
@@ -1898,11 +1935,11 @@ proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffec
 proc strip*(s: string, leading = true, trailing = true,
             runes: openarray[Rune] = unicodeSpaces): string {.noSideEffect,
             rtl, extern: "nucStrip".} =
-  ## Strips leading or trailing `runes` from `s` and returns
+  ## Strips leading or trailing ``runes`` from ``s`` and returns
   ## the resulting string.
   ##
-  ## If `leading` is true, leading `runes` are stripped.
-  ## If `trailing` is true, trailing `runes` are stripped.
+  ## If ``leading`` is true, leading ``runes`` are stripped.
+  ## If ``trailing`` is true, trailing ``runes`` are stripped.
   ## If both are false, the string is returned unchanged.
   var
     s_i = 0 ## starting index into string ``s``
@@ -1948,9 +1985,9 @@ proc strip*(s: string, leading = true, trailing = true,
 
 proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
   rtl, extern: "nucRepeatRune".} =
-  ## Returns a string of `count` Runes `c`.
+  ## Returns a string of ``count`` Runes ``c``.
   ##
-  ## The returned string will have a rune-length of `count`.
+  ## The returned string will have a rune-length of ``count``.
   let s = $c
   result = newStringOfCap(count * s.len)
   for i in 0 ..< count:
@@ -1958,11 +1995,11 @@ proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
 
 proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
   noSideEffect, rtl, extern: "nucAlignString".} =
-  ## Aligns a unicode string `s` with `padding`, so that it has a rune-length
-  ## of `count`.
+  ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length
+  ## of ``count``.
   ##
-  ## `padding` characters (by default spaces) are added before `s` resulting in
-  ## right alignment. If ``s.runelen >= count``, no spaces are added and `s` is
+  ## ``padding`` characters (by default spaces) are added before ``s`` resulting in
+  ## right alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
   ## returned unchanged. If you need to left align a string use the `alignLeft
   ## proc <#alignLeft>`_.
   runnableExamples:
@@ -1985,11 +2022,11 @@ proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
 
 proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.
     noSideEffect.} =
-  ## Left-Aligns a unicode string `s` with `padding`, so that it has a
-  ## rune-length of `count`.
+  ## Left-Aligns a unicode string ``s`` with ``padding``, so that it has a
+  ## rune-length of ``count``.
   ##
-  ## `padding` characters (by default spaces) are added after `s` resulting in
-  ## left alignment. If ``s.runelen >= count``, no spaces are added and `s` is
+  ## ``padding`` characters (by default spaces) are added after ``s`` resulting in
+  ## left alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
   ## returned unchanged. If you need to right align a string use the `align
   ## proc <#align>`_.
   runnableExamples:
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 135a24e9a..ce147cccc 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -499,7 +499,7 @@ template test*(name, body) {.dirty.} =
 
     finally:
       if testStatusIMPL == FAILED:
-        programResult += 1
+        programResult = 1
       let testResult = TestResult(
         suiteName: when declared(testSuiteName): testSuiteName else: "",
         testName: name,
@@ -540,7 +540,7 @@ template fail* =
   when declared(testStatusIMPL):
     testStatusIMPL = FAILED
   else:
-    programResult += 1
+    programResult = 1
 
   ensureInitialized()
 
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d296017dd..bfb411fc8 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -8,6 +8,35 @@
 #
 
 ## This module implements URI parsing as specified by RFC 3986.
+##
+## A Uniform Resource Identifier (URI) provides a simple and extensible
+## means for identifying a resource. A URI can be further classified
+## as a locator, a name, or both. The term “Uniform Resource Locator”
+## (URL) refers to the subset of URIs.
+##
+## Basic usage
+## ===========
+##
+## Combine URIs
+## -------------
+## .. code-block::
+##    import uri
+##    let host = parseUri("https://nim-lang.org")
+##    let blog = "/blog.html"
+##    let bloguri = host / blog
+##    assert $host == "https://nim-lang.org"
+##    assert $bloguri == "https://nim-lang.org/blog.html"
+##
+## Access URI item
+## ---------------
+## .. code-block::
+##    import uri
+##    let res = parseUri("sftp://127.0.0.1:4343")
+##    if isAbsolute(res):
+##      echo "Connect to port: " & res.port
+##      # --> Connect to port: 4343
+##    else:
+##      echo "Wrong format"
 
 import strutils, parseutils
 type
@@ -18,44 +47,24 @@ type
     hostname*, port*, path*, query*, anchor*: string
     opaque*: bool
 
-{.push warning[deprecated]: off.}
-proc `$`*(url: Url): string {.deprecated: "use Uri instead".} =
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  return string(url)
-
-proc `/`*(a, b: Url): Url {.deprecated: "use Uri instead".} =
-  ## Joins two URLs together, separating them with / if needed.
-  ##
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  var urlS = $a
-  var bS = $b
-  if urlS == "": return b
-  if urlS[urlS.len-1] != '/':
-    urlS.add('/')
-  if bS[0] == '/':
-    urlS.add(bS.substr(1))
-  else:
-    urlS.add(bs)
-  result = Url(urlS)
-
-proc add*(url: var Url, a: Url) {.deprecated: "use Uri instead".} =
-  ## Appends url to url.
-  ##
-  ## **Deprecated since 0.9.6**: Use ``Uri`` instead.
-  url = url / a
-{.pop.}
-
 proc encodeUrl*(s: string, usePlus=true): string =
   ## Encodes a URL according to RFC3986.
   ##
   ## This means that characters in the set
   ## ``{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}`` are
   ## carried over to the result.
-  ## All other characters are encoded as ``''%xx'`` where ``xx``
+  ## All other characters are encoded as ``%xx`` where ``xx``
   ## denotes its hexadecimal value.
   ##
   ## As a special rule, when the value of ``usePlus`` is true,
-  ## spaces are encoded as ``'+'`` instead of ``'%20'``.
+  ## spaces are encoded as ``+`` instead of ``%20``.
+  ##
+  ## **See also:**
+  ## * `decodeUrl proc<#decodeUrl,string>`_
+  runnableExamples:
+    assert encodeUrl("https://nim-lang.org") == "https%3A%2F%2Fnim-lang.org"
+    assert encodeUrl("https://nim-lang.org/this is a test") == "https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test"
+    assert encodeUrl("https://nim-lang.org/this is a test", false) == "https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test"
   result = newStringOfCap(s.len + s.len shr 2) # assume 12% non-alnum-chars
   let fromSpace = if usePlus: "+" else: "%20"
   for c in s:
@@ -70,12 +79,19 @@ proc encodeUrl*(s: string, usePlus=true): string =
 proc decodeUrl*(s: string, decodePlus=true): string =
   ## Decodes a URL according to RFC3986.
   ##
-  ## This means that any ``'%xx'`` (where ``xx`` denotes a hexadecimal
+  ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal
   ## value) are converted to the character with ordinal number ``xx``,
   ## and every other character is carried over.
   ##
-  ## As a special rule, when the value of ``decodePlus`` is true, ``'+'``
+  ## As a special rule, when the value of ``decodePlus`` is true, ``+``
   ## characters are converted to a space.
+  ##
+  ## **See also:**
+  ## * `encodeUrl proc<#encodeUrl,string>`_
+  runnableExamples:
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org") == "https://nim-lang.org"
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test") == "https://nim-lang.org/this is a test"
+    assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test", false) == "https://nim-lang.org/this is a test"
   proc handleHexChar(c: char, x: var int) {.inline.} =
     case c
     of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
@@ -150,7 +166,14 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
     i.inc parseUntil(uri, result.anchor, {}, i)
 
 proc initUri*(): Uri =
-  ## Initializes a URI.
+  ## Initializes a URI with ``scheme``, ``username``, ``password``,
+  ## ``hostname``, ``port``, ``path``, ``query`` and ``anchor``.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  runnableExamples:
+    var uri: Uri
+    assert initUri() == uri
   result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
                 path: "", query: "", anchor: "")
 
@@ -163,6 +186,16 @@ proc resetUri(uri: var Uri) =
 
 proc parseUri*(uri: string, result: var Uri) =
   ## Parses a URI. The `result` variable will be cleared before.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  ## * `initUri proc <#initUri,>`_ for initializing a URI
+  runnableExamples:
+    var res = initUri()
+    parseUri("https://nim-lang.org/docs/manual.html", res)
+    assert res.scheme == "https"
+    assert res.hostname == "nim-lang.org"
+    assert res.path == "/docs/manual.html"
   resetUri(result)
 
   var i = 0
@@ -201,6 +234,14 @@ proc parseUri*(uri: string, result: var Uri) =
 
 proc parseUri*(uri: string): Uri =
   ## Parses a URI and returns it.
+  ##
+  ## **See also:**
+  ## * `Uri type <#Uri>`_ for available fields in the URI type
+  runnableExamples:
+    let res = parseUri("ftp://Username:Password@Hostname")
+    assert res.username == "Username"
+    assert res.password == "Password"
+    assert res.scheme == "ftp"
   result = initUri()
   parseUri(uri, result)
 
@@ -251,22 +292,18 @@ proc combine*(base: Uri, reference: Uri): Uri =
   ## This uses the algorithm specified in
   ## `section 5.2.2 of RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2.2>`_.
   ##
-  ## This means that the slashes inside the base URI's path as well as reference
-  ## URI's path affect the resulting URI.
-  ##
-  ## For building URIs you may wish to use \`/\` instead.
-  ##
-  ## Examples:
-  ##
-  ## .. code-block::
-  ##   let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
-  ##   assert foo.path == "/baz"
-  ##
-  ##   let bar = combine(parseUri("http://example.com/foo/bar"), parseUri("baz"))
-  ##   assert bar.path == "/foo/baz"
+  ## This means that the slashes inside the base URIs path as well as reference
+  ## URIs path affect the resulting URI.
   ##
-  ##   let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz"))
-  ##   assert bar.path == "/foo/bar/baz"
+  ## **See also:**
+  ## * `/ proc <#/,Uri,string>`_ for building URIs
+  runnableExamples:
+    let foo = combine(parseUri("https://nim-lang.org/foo/bar"), parseUri("/baz"))
+    assert foo.path == "/baz"
+    let bar = combine(parseUri("https://nim-lang.org/foo/bar"), parseUri("baz"))
+    assert bar.path == "/foo/baz"
+    let qux = combine(parseUri("https://nim-lang.org/foo/bar/"), parseUri("baz"))
+    assert qux.path == "/foo/bar/baz"
 
   template setAuthority(dest, src): untyped =
     dest.hostname = src.hostname
@@ -302,32 +339,42 @@ proc combine*(base: Uri, reference: Uri): Uri =
 
 proc combine*(uris: varargs[Uri]): Uri =
   ## Combines multiple URIs together.
+  ##
+  ## **See also:**
+  ## * `/ proc <#/,Uri,string>`_ for building URIs
+  runnableExamples:
+    let foo = combine(parseUri("https://nim-lang.org/blog.html"), parseUri("/install.html"))
+    assert foo.hostname == "nim-lang.org"
+    assert foo.path == "/install.html"
   result = uris[0]
   for i in 1 ..< uris.len:
     result = combine(result, uris[i])
 
 proc isAbsolute*(uri: Uri): bool =
-  ## returns true if URI is absolute, false otherwise
+  ## Returns true if URI is absolute, false otherwise.
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org")
+    assert isAbsolute(foo) == true
+    let bar = parseUri("nim-lang")
+    assert isAbsolute(bar) == false
   return uri.scheme != "" and (uri.hostname != "" or uri.path != "")
 
 proc `/`*(x: Uri, path: string): Uri =
-  ## Concatenates the path specified to the specified URI's path.
+  ## Concatenates the path specified to the specified URIs path.
   ##
-  ## Contrary to the ``combine`` procedure you do not have to worry about
-  ## the slashes at the beginning and end of the path and URI's path
+  ## Contrary to the `combine proc <#combine,Uri,Uri>`_ you do not have to worry about
+  ## the slashes at the beginning and end of the path and URIs path
   ## respectively.
   ##
-  ## Examples:
-  ##
-  ## .. code-block::
-  ##   let foo = parseUri("http://example.com/foo/bar") / "/baz"
-  ##   assert foo.path == "/foo/bar/baz"
-  ##
-  ##   let bar = parseUri("http://example.com/foo/bar") / "baz"
-  ##   assert bar.path == "/foo/bar/baz"
-  ##
-  ##   let bar = parseUri("http://example.com/foo/bar/") / "baz"
-  ##   assert bar.path == "/foo/bar/baz"
+  ## **See also:**
+  ## * `combine proc <#combine,Uri,Uri>`_
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org/foo/bar") / "/baz"
+    assert foo.path == "/foo/bar/baz"
+    let bar = parseUri("https://nim-lang.org/foo/bar") / "baz"
+    assert bar.path == "/foo/bar/baz"
+    let qux = parseUri("https://nim-lang.org/foo/bar/") / "baz"
+    assert qux.path == "/foo/bar/baz"
   result = x
 
   if result.path.len == 0:
@@ -348,6 +395,9 @@ proc `/`*(x: Uri, path: string): Uri =
 
 proc `$`*(u: Uri): string =
   ## Returns the string representation of the specified URI object.
+  runnableExamples:
+    let foo = parseUri("https://nim-lang.org")
+    assert $foo == "https://nim-lang.org"
   result = ""
   if u.scheme.len > 0:
     result.add(u.scheme)
diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim
index e7e6697cf..b5660f244 100644
--- a/lib/std/sha1.nim
+++ b/lib/std/sha1.nim
@@ -7,7 +7,32 @@
 #    distribution, for details about the copyright.
 #
 
-## Note: Import ``std/sha1`` to use this module
+## **Note:** Import ``std/sha1`` to use this module
+##
+## SHA-1 (Secure Hash Algorithm 1) is a cryptographic hash function which
+## takes an input and produces a 160-bit (20-byte) hash value known as a
+## message digest.
+##
+## .. code-block::
+##    import std/sha1
+##
+##    let accessName = secureHash("John Doe")
+##    assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362"
+##
+## .. code-block::
+##    import std/sha1
+##
+##    let
+##      a = secureHashFile("myFile.nim")
+##      b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C")
+##
+##    if a == b:
+##      echo "Files match"
+##
+## **See also:**
+## * `base64 module<base64.html>`_ implements a base64 encoder and decoder
+## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
+## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
 
 import strutils
 from endians import bigEndian32, bigEndian64
@@ -170,23 +195,61 @@ proc finalize(ctx: var Sha1State): Sha1Digest =
 # Public API
 
 proc secureHash*(str: string): SecureHash =
+  ## Generates a ``SecureHash`` from a ``str``.
+  ##
+  ## **See also:**
+  ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file
+  ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash``
+  runnableExamples:
+    let hash = secureHash("Hello World")
+    assert hash == parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0")
   var state = newSha1State()
   state.update(str)
   SecureHash(state.finalize())
 
 proc secureHashFile*(filename: string): SecureHash =
+  ## Generates a ``SecureHash`` from a file.
+  ##
+  ## **See also:**
+  ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
+  ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash``
   secureHash(readFile(filename))
 
 proc `$`*(self: SecureHash): string =
+  ## Returns the string representation of a ``SecureHash``.
+  ##
+  ## **See also:**
+  ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
+  runnableExamples:
+    let hash = secureHash("Hello World")
+    assert $hash == "0A4D55A8D778E5022FAB701977C5D840BBC486D0"
   result = ""
   for v in Sha1Digest(self):
     result.add(toHex(int(v), 2))
 
 proc parseSecureHash*(hash: string): SecureHash =
+  ## Converts a string ``hash`` to ``SecureHash``.
+  ##
+  ## **See also:**
+  ## * `secureHash proc <#secureHash,string>`_ for generating a ``SecureHash`` from a string
+  ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file
+  runnableExamples:
+    let
+      hashStr = "0A4D55A8D778E5022FAB701977C5D840BBC486D0"
+      secureHash = secureHash("Hello World")
+    assert secureHash == parseSecureHash(hashStr)
   for i in 0 ..< Sha1DigestSize:
     Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
 
 proc `==`*(a, b: SecureHash): bool =
+  ## Checks if two ``SecureHash`` values are identical.
+  runnableExamples:
+    let
+      a = secureHash("Hello World")
+      b = secureHash("Goodbye World")
+      c = parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0")
+    assert a != b
+    assert a == c
   # Not a constant-time comparison, but that's acceptable in this context
   Sha1Digest(a) == Sha1Digest(b)
 
diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim
new file mode 100644
index 000000000..37918ee6c
--- /dev/null
+++ b/lib/std/time_t.nim
@@ -0,0 +1,23 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+when defined(nimdoc):
+  type
+    impl = distinct int64
+    Time* = impl ## \
+      ## Wrapper for ``time_t``. On posix, this is an alias to ``posix.Time``.
+elif defined(windows):
+  when defined(i386) and defined(gcc):
+    type Time* {.importc: "time_t", header: "<time.h>".} = distinct int32
+  else:
+    # newest version of Visual C++ defines time_t to be of 64 bits
+    type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64
+elif defined(posix):
+  import posix
+  export posix.Time
\ No newline at end of file
diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim
index c7898b339..4b0dc4417 100644
--- a/lib/std/wordwrap.nim
+++ b/lib/std/wordwrap.nim
@@ -24,6 +24,11 @@ proc wrapWords*(s: string, maxLineWidth = 80,
                seps: set[char] = Whitespace,
                newLine = "\n"): string {.noSideEffect.} =
   ## Word wraps `s`.
+  runnableExamples:
+    doAssert "12345678901234567890".wrapWords() == "12345678901234567890"
+    doAssert "123456789012345678901234567890".wrapWords(20) == "12345678901234567890\n1234567890"
+    doAssert "Hello Bob. Hello John.".wrapWords(13, false) == "Hello Bob.\nHello John."
+    doAssert "Hello Bob. Hello John.".wrapWords(13, true, {';'}) == "Hello Bob. He\nllo John."
   result = newStringOfCap(s.len + s.len shr 6)
   var spaceLeft = maxLineWidth
   var lastSep = ""
diff --git a/lib/system.nim b/lib/system.nim
index ea767e27a..d4a86e53b 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -118,6 +118,22 @@ proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
   ##     # Do here programmer friendly expensive sanity checks.
   ##   # Put here the normal code
 
+when defined(nimHasRunnableExamples):
+  proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".}
+    ## A section you should use to mark `runnable example`:idx: code with.
+    ##
+    ## - In normal debug and release builds code within
+    ##   a ``runnableExamples`` section is ignored.
+    ## - The documentation generator is aware of these examples and considers them
+    ##   part of the ``##`` doc comment. As the last step of documentation
+    ##   generation the examples are put into an ``$file_example.nim`` file,
+    ##   compiled and tested. The collected examples are
+    ##   put into their own module to ensure the examples do not refer to
+    ##   non-exported symbols.
+else:
+  template runnableExamples*(body: untyped) =
+    discard
+
 proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
   ## Special compile-time procedure that checks whether `x` is
   ## declared. `x` has to be an identifier or a qualified identifier.
@@ -553,7 +569,12 @@ type
       trace: string
     else:
       trace: seq[StackTraceEntry]
-    raiseId: uint # set when exception is raised
+    when defined(nimBoostrapCsources0_19_0):
+      # see #10315, bootstrap with `nim cpp` from csources gave error:
+      # error: no member named 'raise_id' in 'Exception'
+      raise_id: uint # set when exception is raised
+    else:
+      raiseId: uint # set when exception is raised
     up: ref Exception # used for stacking exceptions. Not exported!
 
   Defect* = object of Exception ## \
@@ -590,7 +611,7 @@ type
     ## Raised when assertion is proved wrong.
     ##
     ## Usually the result of using the `assert() template <#assert>`_.
-  ValueError* = object of Defect ## \
+  ValueError* = object of CatchableError ## \
     ## Raised for string and object conversion errors.
   KeyError* = object of ValueError ## \
     ## Raised if a key cannot be found in a table.
@@ -1479,12 +1500,9 @@ const
   # for string literals, it allows for some optimizations.
 
 {.push profiler: off.}
-when defined(nimKnowsNimvm):
-  let nimvm* {.magic: "Nimvm".}: bool = false
-    ## may be used only in "when" expression.
-    ## It is true in Nim VM context and false otherwise
-else:
-  const nimvm*: bool = false
+let nimvm* {.magic: "Nimvm", compileTime.}: bool = false
+  ## may be used only in "when" expression.
+  ## It is true in Nim VM context and false otherwise
 {.pop.}
 
 proc compileOption*(option: string): bool {.
@@ -1539,7 +1557,7 @@ else:
                                         ## ``string`` if the taint mode is not
                                         ## turned on.
 
-when defined(profiler):
+when defined(profiler) and not defined(nimscript):
   proc nimProfile() {.compilerProc, noinline.}
 when hasThreadSupport:
   {.pragma: rtlThreadVar, threadvar.}
@@ -1555,7 +1573,7 @@ const
     ## is the value that should be passed to `quit <#quit>`_ to indicate
     ## failure.
 
-when defined(nodejs):
+when defined(nodejs) and not defined(nimscript):
   var programResult* {.importc: "process.exitCode".}: int
   programResult = 0
 else:
@@ -1601,7 +1619,7 @@ elif defined(genode):
 
 
 
-elif defined(nodejs):
+elif defined(nodejs) and not defined(nimscript):
   proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
     importc: "process.exit", noreturn.}
 
@@ -1692,7 +1710,7 @@ proc insert*[T](x: var seq[T], item: T, i = 0.Natural) {.noSideEffect.} =
   else:
     when defined(js):
       var it : T
-      {.emit: "`x`.splice(`i`, 0, `it`);".}
+      {.emit: "`x` = `x` || []; `x`.splice(`i`, 0, `it`);".}
     else:
       defaultImpl()
   x[i] = item
@@ -1795,21 +1813,26 @@ proc toFloat*(i: int): float {.
 
 proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.
   magic: "ToBiggestFloat", noSideEffect, importc: "toBiggestFloat".}
-  ## converts an biggestint `i` into a ``biggestfloat``. If the conversion
+  ## converts a biggestint `i` into a ``biggestfloat``. If the conversion
   ## fails, `ValueError` is raised. However, on most platforms the
   ## conversion cannot fail.
 
 proc toInt*(f: float): int {.
-  magic: "ToInt", noSideEffect, importc: "toInt".}
+  magic: "ToInt", noSideEffect, importc: "toInt".} =
   ## converts a floating point number `f` into an ``int``. Conversion
-  ## rounds `f` if it does not contain an integer value. If the conversion
-  ## fails (because `f` is infinite for example), `ValueError` is raised.
+  ## rounds `f` half away from 0, see https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero
+  ## Note that some floating point numbers (e.g. infinity or even 1e19)
+  ## cannot be accurately converted.
+  runnableExamples:
+    doAssert toInt(0.49) == 0
+    doAssert toInt(0.5) == 1
+    doAssert toInt(-0.5) == -1 ## rounding is symmetrical
 
 proc toBiggestInt*(f: BiggestFloat): BiggestInt {.
-  magic: "ToBiggestInt", noSideEffect, importc: "toBiggestInt".}
-  ## converts a biggestfloat `f` into a ``biggestint``. Conversion
-  ## rounds `f` if it does not contain an integer value. If the conversion
-  ## fails (because `f` is infinite for example), `ValueError` is raised.
+  magic: "ToBiggestInt", noSideEffect, importc: "toBiggestInt".} =
+  ## Same as `toInt` but for BiggestFloat to ``BiggestInt``.
+  runnableExamples:
+    doAssert toBiggestInt(0.49) == 0
 
 proc addQuitProc*(quitProc: proc() {.noconv.}) {.
   importc: "atexit", header: "<stdlib.h>".}
@@ -2624,8 +2647,8 @@ proc `==`*[T: tuple|object](x, y: T): bool =
   return true
 
 proc `<=`*[T: tuple](x, y: T): bool =
-  ## generic ``<=`` operator for tuples that is lifted from the components
-  ## of `x` and `y`. This implementation uses `cmp`.
+  ## generic lexicographic ``<=`` operator for tuples that is lifted from the
+  ## components of `x` and `y`. This implementation uses `cmp`.
   for a, b in fields(x, y):
     var c = cmp(a, b)
     if c < 0: return true
@@ -2633,8 +2656,8 @@ proc `<=`*[T: tuple](x, y: T): bool =
   return true
 
 proc `<`*[T: tuple](x, y: T): bool =
-  ## generic ``<`` operator for tuples that is lifted from the components
-  ## of `x` and `y`. This implementation uses `cmp`.
+  ## generic lexicographic ``<`` operator for tuples that is lifted from the
+  ## components of `x` and `y`. This implementation uses `cmp`.
   for a, b in fields(x, y):
     var c = cmp(a, b)
     if c < 0: return true
@@ -2651,19 +2674,28 @@ proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.
   ##     echo "'+' for integers is available"
   discard
 
+include "system/helpers" # for `lineInfoToString`, `isNamedTuple`
+
 proc `$`*[T: tuple|object](x: T): string =
   ## generic ``$`` operator for tuples that is lifted from the components
   ## of `x`. Example:
   ##
   ## .. code-block:: nim
   ##   $(23, 45) == "(23, 45)"
+  ##   $(a: 23, b: 45) == "(a: 23, b: 45)"
   ##   $() == "()"
   result = "("
   var firstElement = true
+  const isNamed = T is object or isNamedTuple(T)
+  when not isNamed:
+    var count = 0
   for name, value in fieldPairs(x):
     if not firstElement: result.add(", ")
-    result.add(name)
-    result.add(": ")
+    when isNamed:
+      result.add(name)
+      result.add(": ")
+    else:
+      count.inc
     when compiles($value):
       when value isnot string and value isnot seq and compiles(value.isNil):
         if value.isNil: result.add "nil"
@@ -2673,6 +2705,11 @@ proc `$`*[T: tuple|object](x: T): string =
       firstElement = false
     else:
       result.add("...")
+      firstElement = false
+  when not isNamed:
+    if count == 1:
+      result.add(",") # $(1,) should print as the semantically legal (1,)
+
   result.add(")")
 
 proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
@@ -2711,6 +2748,16 @@ proc `$`*[T](x: seq[T]): string =
   ##   $(@[23, 45]) == "@[23, 45]"
   collectionToString(x, "@[", ", ", "]")
 
+proc `$`*[T, U](x: HSlice[T, U]): string =
+  ## generic ``$`` operator for slices that is lifted from the components
+  ## of `x`. Example:
+  ##
+  ## .. code-block:: nim
+  ##  $(1 .. 5) == "1 .. 5"
+  result = $x.a
+  result.add(" .. ")
+  result.add($x.b)
+
 # ----------------- GC interface ---------------------------------------------
 
 when not defined(nimscript) and hasAlloc:
@@ -2760,8 +2807,8 @@ when not defined(nimscript) and hasAlloc:
 
     when not defined(JS) and not defined(nimscript) and hasAlloc:
       proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign.}
-      ## Expands operating GC stack range to `theStackBottom`. Does nothing
-      ## if current stack bottom is already lower than `theStackBottom`.
+        ## Expands operating GC stack range to `theStackBottom`. Does nothing
+        ## if current stack bottom is already lower than `theStackBottom`.
 
   else:
     template GC_disable* =
@@ -3075,7 +3122,7 @@ when not defined(JS): #and not defined(nimscript):
     proc initStackBottom() {.inline, compilerproc.} =
       # WARNING: This is very fragile! An array size of 8 does not work on my
       # Linux 64bit system. -- That's because the stack direction is the other
-      # way round.
+      # way around.
       when declared(nimGC_setStackBottom):
         var locals {.volatile.}: pointer
         locals = addr(locals)
@@ -3147,7 +3194,7 @@ when not defined(JS): #and not defined(nimscript):
         result = x.len - y.len
 
   when defined(nimscript):
-    proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
+    proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
       ## Opens a file named `filename` for reading, calls `readAll
       ## <#readAll>`_ and closes the file afterwards. Returns the string.
       ## Raises an IO exception in case of an error. If # you need to call
@@ -3196,14 +3243,15 @@ when not defined(JS): #and not defined(nimscript):
 
     proc open*(f: var File, filename: string,
                mode: FileMode = fmRead, bufSize: int = -1): bool {.tags: [],
-               benign.}
+               raises: [], benign.}
       ## Opens a file named `filename` with given `mode`.
       ##
       ## Default mode is readonly. Returns true iff the file could be opened.
       ## This throws no exception if the file could not be opened.
 
     proc open*(f: var File, filehandle: FileHandle,
-               mode: FileMode = fmRead): bool {.tags: [], benign.}
+               mode: FileMode = fmRead): bool {.tags: [], raises: [],
+               benign.}
       ## Creates a ``File`` from a `filehandle` with given `mode`.
       ##
       ## Default mode is readonly. Returns true iff the file could be opened.
@@ -3212,7 +3260,7 @@ when not defined(JS): #and not defined(nimscript):
                mode: FileMode = fmRead, bufSize: int = -1): File =
       ## Opens a file named `filename` with given `mode`.
       ##
-      ## Default mode is readonly. Raises an ``IO`` exception if the file
+      ## Default mode is readonly. Raises an ``IOError`` if the file
       ## could not be opened.
       if not open(result, filename, mode, bufSize):
         sysFatal(IOError, "cannot open: ", filename)
@@ -3415,6 +3463,10 @@ when not defined(JS): #and not defined(nimscript):
       ## allows you to override the behaviour of your application when CTRL+C
       ## is pressed. Only one such hook is supported.
 
+    when not defined(useNimRtl):
+      proc unsetControlCHook*()
+        ## reverts a call to setControlCHook
+
     proc writeStackTrace*() {.tags: [], gcsafe.}
       ## writes the current stack trace to ``stderr``. This is only works
       ## for debug builds. Since it's usually used for debugging, this
@@ -3431,6 +3483,10 @@ when not defined(JS): #and not defined(nimscript):
     when defined(memtracker):
       include "system/memtracker"
 
+    when defined(gcDestructors):
+      include "core/strs"
+      include "core/seqs"
+
     when hostOS == "standalone":
       include "system/embedded"
     else:
@@ -3479,10 +3535,7 @@ when not defined(JS): #and not defined(nimscript):
     {.pop.}
     {.push stack_trace: off, profiler:off.}
     when hasAlloc:
-      when defined(gcDestructors):
-        include "core/strs"
-        include "core/seqs"
-      else:
+      when not defined(gcDestructors):
         include "system/sysstr"
     {.pop.}
     when hasAlloc: include "system/strmantle"
@@ -3568,7 +3621,7 @@ when not defined(JS): #and not defined(nimscript):
   when defined(endb) and not defined(nimscript):
     include "system/debugger"
 
-  when defined(profiler) or defined(memProfiler):
+  when (defined(profiler) or defined(memProfiler)) and not defined(nimscript):
     include "system/profiler"
   {.pop.} # stacktrace
 
@@ -3609,7 +3662,7 @@ elif defined(JS):
   proc deallocShared(p: pointer) = discard
   proc reallocShared(p: pointer, newsize: Natural): pointer = discard
 
-  when defined(JS):
+  when defined(JS) and not defined(nimscript):
     include "system/jssys"
     include "system/reprjs"
   elif defined(nimscript):
@@ -3833,15 +3886,21 @@ proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
   ## Same as `gorge` but also returns the precious exit code.
   discard
 
-proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
+proc `+=`*[T: SomeInteger](x: var T, y: T) {.
   magic: "Inc", noSideEffect.}
-  ## Increments an ordinal
+  ## Increments an integer
+
+proc `+=`*[T: enum|bool](x: var T, y: T) {.
+  magic: "Inc", noSideEffect, deprecated: "use `inc` instead".}
 
-proc `-=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
+proc `-=`*[T: SomeInteger](x: var T, y: T) {.
   magic: "Dec", noSideEffect.}
   ## Decrements an ordinal
 
-proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
+proc `-=`*[T: enum|bool](x: var T, y: T) {.
+  magic: "Dec", noSideEffect, deprecated: "0.20.0, use `dec` instead".}
+
+proc `*=`*[T: SomeInteger](x: var T, y: T) {.
   inline, noSideEffect.} =
   ## Binary `*=` operator for ordinals
   x = x * y
@@ -3919,7 +3978,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
 template currentSourcePath*: string = instantiationInfo(-1, true).filename
   ## returns the full file-system path of the current source
 
-proc raiseAssert*(msg: string) {.noinline.} =
+proc raiseAssert*(msg: string) {.noinline, noReturn.} =
   sysFatal(AssertionError, msg)
 
 proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
@@ -3929,8 +3988,6 @@ proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
                                     tags: [].}
   Hide(raiseAssert)(msg)
 
-include "system/helpers" # for `lineInfoToString`
-
 template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) =
   const loc = $instantiationInfo(-1, true)
   bind instantiationInfo
@@ -4326,24 +4383,7 @@ when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage):
     importc: "SetConsoleOutputCP".}
   discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage
 
-
-when defined(nimHasRunnableExamples):
-  proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".}
-    ## A section you should use to mark `runnable example`:idx: code with.
-    ##
-    ## - In normal debug and release builds code within
-    ##   a ``runnableExamples`` section is ignored.
-    ## - The documentation generator is aware of these examples and considers them
-    ##   part of the ``##`` doc comment. As the last step of documentation
-    ##   generation the examples are put into an ``$file_example.nim`` file,
-    ##   compiled and tested. The collected examples are
-    ##   put into their own module to ensure the examples do not refer to
-    ##   non-exported symbols.
-else:
-  template runnableExamples*(body: untyped) =
-    discard
-
-template doAssertRaises*(exception, code: untyped): typed =
+template doAssertRaises*(exception: typedesc, code: untyped): typed =
   ## Raises ``AssertionError`` if specified ``code`` does not raise the
   ## specified exception. Example:
   ##
@@ -4351,16 +4391,24 @@ template doAssertRaises*(exception, code: untyped): typed =
   ##  doAssertRaises(ValueError):
   ##    raise newException(ValueError, "Hello World")
   var wrong = false
-  try:
-    if true:
-      code
-    wrong = true
-  except exception:
-    discard
-  except Exception as exc:
-    raiseAssert(astToStr(exception) &
-                " wasn't raised, another error was raised instead by:\n"&
-                astToStr(code))
+  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 as exc:
+      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))
 
@@ -4420,3 +4468,12 @@ when defined(genode):
       componentConstructHook(env)
         # Perform application initialization
         # and return to thread entrypoint.
+
+proc `$`*(t: typedesc): string {.magic: "TypeTrait".} =
+  ## Returns the name of the given type.
+  ##
+  ## For more procedures dealing with ``typedesc``, see ``typetraits.nim``.
+  runnableExamples:
+    doAssert $(type(42)) == "int"
+    doAssert $(type("Foo")) == "string"
+    static: doAssert $(type(@['A', 'B'])) == "seq[char]"
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index b090117a9..e938dc475 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -20,7 +20,7 @@ template track(op, address, size) =
 # Each chunk starts at an address that is divisible by the page size.
 
 const
-  InitialMemoryRequest = 128 * PageSize # 0.5 MB
+  nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
   SmallChunkSize = PageSize
   MaxFli = 30
   MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
@@ -588,8 +588,8 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
   result = findSuitableBlock(a, fl, sl)
   if result == nil:
-    if size < InitialMemoryRequest:
-      result = requestOsChunks(a, InitialMemoryRequest)
+    if size < nimMinHeapPages * PageSize:
+      result = requestOsChunks(a, nimMinHeapPages * PageSize)
       splitChunk(a, result, size)
     else:
       result = requestOsChunks(a, size)
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 38910dbd6..af34060d8 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -51,7 +51,7 @@ when defined(windows):
 elif defined(macosx) or defined(linux) or defined(freebsd) or
      defined(openbsd) or defined(netbsd) or defined(solaris) or
      defined(dragonfly) or defined(nintendoswitch) or defined(genode) or
-     defined(aix):
+     defined(aix) or hostOS == "standalone":
   const
     SIGABRT = cint(6)
     SIGFPE = cint(8)
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index 4d453fcca..fb89e7f0f 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -40,6 +40,7 @@ proc reraiseException() {.compilerRtl.} =
 
 proc writeStackTrace() = discard
 
+proc unsetControlCHook() = discard
 proc setControlCHook(hook: proc () {.noconv.}) = discard
 
 proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 84a1da343..3efefead4 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -536,3 +536,8 @@ proc setControlCHook(hook: proc () {.noconv.}) =
   # ugly cast, but should work on all architectures:
   type SignalHandler = proc (sign: cint) {.noconv, benign.}
   c_signal(SIGINT, cast[SignalHandler](hook))
+
+when not defined(useNimRtl):
+  proc unsetControlCHook() =
+    # proc to unset a hook set by setControlCHook
+    c_signal(SIGINT, signalHandler)
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index fb20edbbb..9ae388aa9 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -17,9 +17,9 @@
 const
   CycleIncrease = 2 # is a multiplicative increase
   InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
-  ZctThreshold = 500  # we collect garbage if the ZCT's size
-                      # reaches this threshold
-                      # this seems to be a good value
+  InitialZctThreshold = 500  # we collect garbage if the ZCT's size
+                             # reaches this threshold
+                             # this seems to be a good value
   withRealTime = defined(useRealtimeGC)
 
 when withRealTime and not declared(getTicks):
@@ -78,6 +78,7 @@ type
     when nimCoroutines:
       activeStack: ptr GcStack    # current executing coroutine stack.
     cycleThreshold: int
+    zctThreshold: int
     when useCellIds:
       idGenerator: int
     zct: CellSeq             # the zero count table
@@ -253,6 +254,7 @@ proc initGC() =
     when traceGC:
       for i in low(CellState)..high(CellState): init(states[i])
     gch.cycleThreshold = InitialCycleThreshold
+    gch.zctThreshold = InitialZctThreshold
     gch.stat.stackScans = 0
     gch.stat.cycleCollections = 0
     gch.stat.maxThreshold = 0
@@ -771,11 +773,7 @@ proc collectCTBody(gch: var GcHeap) =
         c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
 
 proc collectCT(gch: var GcHeap) =
-  # stackMarkCosts prevents some pathological behaviour: Stack marking
-  # becomes more expensive with large stacks and large stacks mean that
-  # cells with RC=0 are more likely to be kept alive by the stack.
-  let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
-  if (gch.zct.len >= stackMarkCosts or (cycleGC and
+  if (gch.zct.len >= gch.zctThreshold or (cycleGC and
       getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
       gch.recGcLock == 0:
     when false:
@@ -783,6 +781,7 @@ proc collectCT(gch: var GcHeap) =
       cellsetReset(gch.marked)
       markForDebug(gch)
     collectCTBody(gch)
+    gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
 
 when withRealTime:
   proc toNano(x: int): Nanos {.inline.} =
@@ -793,10 +792,11 @@ when withRealTime:
 
   proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
     gch.maxPause = us.toNano
-    if (gch.zct.len >= ZctThreshold or (cycleGC and
+    if (gch.zct.len >= gch.zctThreshold or (cycleGC and
         getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
         strongAdvice:
       collectCTBody(gch)
+      gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
 
   proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
     if stackSize >= 0:
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 565453298..ebd3dada2 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -418,7 +418,7 @@ proc prepareDealloc(cell: PCell) =
   decTypeSize(cell, t)
 
 proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
-  ## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
+  ## Frees the thread local heap. Runs every finalizer if ``runFinalizers``
   ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
   ## happens to ensure the GC can continue to work after the call
   ## to ``deallocHeap``.
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index d8cb3e2d0..aa5fb6aea 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -485,8 +485,9 @@ proc collectCTBody(gch: var GcHeap) =
   sysAssert(allocInv(gch.region), "collectCT: end")
 
 proc collectCT(gch: var GcHeap; size: int) =
+  let fmem = getFreeMem(gch.region)
   if (getOccupiedMem(gch.region) >= gch.cycleThreshold or
-      size > getFreeMem(gch.region)) and gch.recGcLock == 0:
+      size > fmem and fmem > InitialThreshold) and gch.recGcLock == 0:
     collectCTBody(gch)
 
 when not defined(useNimRtl):
diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim
index 8a1446944..3b908fb08 100644
--- a/lib/system/gc_regions.nim
+++ b/lib/system/gc_regions.nim
@@ -23,6 +23,21 @@ when defined(nimphpext):
   proc osDeallocPages(p: pointer, size: int) {.inline.} =
     efree(p)
 
+elif defined(useMalloc):
+  proc roundup(x, v: int): int {.inline.} =
+    result = (x + (v-1)) and not (v-1)
+  proc emalloc(size: int): pointer {.importc: "malloc", header: "<stdlib.h>".}
+  proc efree(mem: pointer) {.importc: "free", header: "<stdlib.h>".}
+
+  proc osAllocPages(size: int): pointer {.inline.} =
+    emalloc(size)
+
+  proc osTryAllocPages(size: int): pointer {.inline.} =
+    emalloc(size)
+
+  proc osDeallocPages(p: pointer, size: int) {.inline.} =
+    efree(p)
+
 else:
   include osalloc
 
@@ -108,6 +123,8 @@ template `+!`(p: pointer, s: int): pointer =
 template `-!`(p: pointer, s: int): pointer =
   cast[pointer](cast[int](p) -% s)
 
+const nimMinHeapPages {.intdefine.} = 4
+
 proc allocSlowPath(r: var MemRegion; size: int) =
   # we need to ensure that the underlying linked list
   # stays small. Say we want to grab 16GB of RAM with some
@@ -116,9 +133,8 @@ proc allocSlowPath(r: var MemRegion; size: int) =
   # 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB,
   # 16GB --> list contains only 20 elements! That's reasonable.
   if (r.totalSize and 1) == 0:
-    r.nextChunkSize =
-      if r.totalSize < 64 * 1024: PageSize*4
-      else: r.nextChunkSize*2
+    r.nextChunkSize = if r.totalSize < 64 * 1024: PageSize*nimMinHeapPages
+                      else: r.nextChunkSize*2
   var s = roundup(size+sizeof(BaseChunk), PageSize)
   var fresh: Chunk
   if s > r.nextChunkSize:
@@ -242,6 +258,13 @@ proc deallocAll*() = tlRegion.deallocAll()
 proc deallocOsPages(r: var MemRegion) = r.deallocAll()
 
 template withScratchRegion*(body: untyped) =
+  let obs = obstackPtr()
+  try:
+    body
+  finally:
+    setObstackPtr(obs)
+
+when false:
   var scratch: MemRegion
   let oldRegion = tlRegion
   tlRegion = scratch
diff --git a/lib/system/helpers.nim b/lib/system/helpers.nim
index fb1218684..a7e47915e 100644
--- a/lib/system/helpers.nim
+++ b/lib/system/helpers.nim
@@ -1,11 +1,28 @@
-## helpers used system.nim and other modules, avoids code duplication while
-## also minimizing symbols exposed in system.nim
+# helpers used system.nim and other modules, avoids code duplication while
+# also minimizing symbols exposed in system.nim
 #
 # TODO: move other things here that should not be exposed in system.nim
 
 proc lineInfoToString(file: string, line, column: int): string =
   file & "(" & $line & ", " & $column & ")"
 
-proc `$`(info: type(instantiationInfo(0))): string =
+type InstantiationInfo = tuple[filename: string, line: int, column: int]
+
+proc `$`(info: InstantiationInfo): string =
   # The +1 is needed here
+  # instead of overriding `$` (and changing its meaning), consider explicit name.
   lineInfoToString(info.fileName, info.line, info.column+1)
+
+proc isNamedTuple(T: type): bool =
+  ## return true for named tuples, false for any other type.
+  when T isnot tuple: result = false
+  else:
+    var t: T
+    for name, _ in t.fieldPairs:
+      when name == "Field0":
+        return compiles(t.Field0)
+      else:
+        return true
+    # empty tuple should be un-named,
+    # see https://github.com/nim-lang/Nim/issues/8861#issue-356631191
+    return false
diff --git a/lib/system/helpers2.nim b/lib/system/helpers2.nim
index 1c9e7c068..c67a2c278 100644
--- a/lib/system/helpers2.nim
+++ b/lib/system/helpers2.nim
@@ -1,3 +1,5 @@
+# imported by other modules, unlike helpers.nim which is included
+
 template formatErrorIndexBound*[T](i, a, b: T): string =
   "index out of bounds: (a:" & $a & ") <= (i:" & $i & ") <= (b:" & $b & ") "
 
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 8be19e5b8..d7718e4f4 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -46,7 +46,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
   result[0] = x
 
 proc isNimException(): bool {.asmNoStackFrame.} =
-  asm "return `lastJSError`.m_type;"
+  asm "return `lastJSError` && `lastJSError`.m_type;"
 
 proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
   if isNimException(): result = cast[ref Exception](lastJSError)
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 89bc11d2c..9cc7ab323 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -72,6 +72,8 @@ when defined(boehmgc):
   proc boehmGCincremental {.
     importc: "GC_enable_incremental", boehmGC.}
   proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.}
+  proc boehmGC_set_all_interior_pointers(flag: cint) {.
+    importc: "GC_set_all_interior_pointers", boehmGC.}
   proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.}
   proc boehmAllocAtomic(size: int): pointer {.
     importc: "GC_malloc_atomic", boehmGC.}
@@ -148,6 +150,7 @@ when defined(boehmgc):
     proc nimGC_setStackBottom(theStackBottom: pointer) = discard
 
   proc initGC() =
+    boehmGC_set_all_interior_pointers(0)
     boehmGCinit()
     when hasThreadSupport:
       boehmGC_allow_register_threads()
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index fc4b574e4..e879fda83 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -46,7 +46,7 @@ proc copyDir(src, dest: string) {.
   tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
 proc createDir(dir: string) {.tags: [WriteIOEffect], raises: [OSError].} =
   builtin
-proc getOsError: string = builtin
+proc getError: string = builtin
 proc setCurrentDir(dir: string) = builtin
 proc getCurrentDir*(): string =
   ## Retrieves the current working directory.
@@ -143,6 +143,7 @@ proc existsDir*(dir: string): bool =
 
 proc selfExe*(): string =
   ## Returns the currently running nim or nimble executable.
+  # TODO: consider making this as deprecated alias of `getCurrentCompilerExe`
   builtin
 
 proc toExe*(filename: string): string =
@@ -177,9 +178,12 @@ var
   mode*: ScriptMode ## Set this to influence how mkDir, rmDir, rmFile etc.
                     ## behave
 
+template checkError(exc: untyped): untyped =
+  let err = getError()
+  if err.len > 0: raise newException(exc, err)
+
 template checkOsError =
-  let err = getOsError()
-  if err.len > 0: raise newException(OSError, err)
+  checkError(OSError)
 
 template log(msg: string, body: untyped) =
   if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
@@ -331,6 +335,26 @@ proc cppDefine*(define: string) =
   ## needs to be mangled.
   builtin
 
+proc stdinReadLine(): TaintedString {.
+  tags: [ReadIOEffect], raises: [IOError].} =
+  builtin
+
+proc stdinReadAll(): TaintedString {.
+  tags: [ReadIOEffect], raises: [IOError].} =
+  builtin
+
+proc readLineFromStdin*(): TaintedString {.raises: [IOError].} =
+  ## Reads a line of data from stdin - blocks until \n or EOF which happens when stdin is closed
+  log "readLineFromStdin":
+    result = stdinReadLine()
+    checkError(EOFError)
+
+proc readAllFromStdin*(): TaintedString {.raises: [IOError].} =
+  ## Reads all data from stdin - blocks until EOF which happens when stdin is closed
+  log "readAllFromStdin":
+    result = stdinReadAll()
+    checkError(EOFError)
+
 when not defined(nimble):
   template `==?`(a, b: string): bool = cmpIgnoreStyle(a, b) == 0
   template task*(name: untyped; description: string; body: untyped): untyped =
@@ -340,14 +364,15 @@ when not defined(nimble):
     ## .. code-block:: nim
     ##  task build, "default build is via the C backend":
     ##    setCommand "c"
-    proc `name Task`*() = body
+    proc `name Task`*() =
+      setCommand "nop"
+      body
 
     let cmd = getCommand()
     if cmd.len == 0 or cmd ==? "help":
       setCommand "help"
       writeTask(astToStr(name), description)
     elif cmd ==? astToStr(name):
-      setCommand "nop"
       `name Task`()
 
   # nimble has its own implementation for these things.
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim
index ffd6fd0c5..0649f1176 100644
--- a/lib/system/profiler.nim
+++ b/lib/system/profiler.nim
@@ -13,6 +13,9 @@
 # (except perhaps loops that have no side-effects). At every Nth call a
 # stack trace is taken. A stack tace is a list of cstrings.
 
+when defined(profiler) and defined(memProfiler):
+  {.error: "profiler and memProfiler cannot be defined at the same time (See Embedded Stack Trace Profiler (ESTP) User Guide) for more details".}
+
 {.push profiler: off.}
 
 const
@@ -57,13 +60,13 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
     b = b.prev
 
 var
-  profilingRequestedHook*: proc (): bool {.nimcall, benign.}
+  profilingRequestedHook*: proc (): bool {.nimcall, locks: 0, gcsafe.}
     ## set this variable to provide a procedure that implements a profiler in
     ## user space. See the `nimprof` module for a reference implementation.
 
 when defined(memProfiler):
   type
-    MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
+    MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, locks: 0, gcsafe.}
 
   var
     profilerHook*: MemProfilerHook
@@ -87,9 +90,10 @@ else:
   proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
     # 'noinline' so that 'nimProfile' does not perform the stack allocation
     # in the common case.
-    var st: StackTrace
-    captureStackTrace(framePtr, st)
-    hook(st)
+    when not defined(nimdoc):
+      var st: StackTrace
+      captureStackTrace(framePtr, st)
+      hook(st)
 
   proc nimProfile() =
     ## This is invoked by the compiler in every loop and on every proc entry!
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index ceaecb4f9..3c681fc53 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Compilerprocs for strings that do not depend on the string implementation.
+# Compilerprocs for strings that do not depend on the string implementation.
 
 proc cmpStrings(a, b: string): int {.inline, compilerProc.} =
   let alen = a.len
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 20964b166..5b0278d74 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -74,10 +74,21 @@ proc raiseEIO(msg: string) {.noinline, noreturn.} =
 proc raiseEOF() {.noinline, noreturn.} =
   sysFatal(EOFError, "EOF reached")
 
+proc strerror(errnum: cint): cstring {.importc, header: "<string.h>".}
+
+when not defined(NimScript):
+  var
+    errno {.importc, header: "<errno.h>".}: cint ## error variable
+
 proc checkErr(f: File) =
-  if c_ferror(f) != 0:
-    c_clearerr(f)
-    raiseEIO("Unknown IO Error")
+  when not defined(NimScript):
+    if c_ferror(f) != 0:
+      let msg = "errno: " & $errno & " `" & $strerror(errno) & "`"
+      c_clearerr(f)
+      raiseEIO(msg)
+  else:
+    # shouldn't happen
+    quit(1)
 
 {.push stackTrace:off, profiler:off.}
 proc readBuffer(f: File, buffer: pointer, len: Natural): int =
diff --git a/lib/system/timers.nim b/lib/system/timers.nim
index dd8350e2d..4cd2fe840 100644
--- a/lib/system/timers.nim
+++ b/lib/system/timers.nim
@@ -49,7 +49,7 @@ elif defined(macosx):
   mach_timebase_info(timeBaseInfo)
 
   proc `-`(a, b: Ticks): Nanos =
-    result = (a.int64 - b.int64)  * timeBaseInfo.numer div timeBaseInfo.denom
+    result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
 
 elif defined(posixRealtime):
   type
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 3e37b824c..6c480d03a 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -516,9 +516,6 @@ type
     fd_count*: cint # unsigned
     fd_array*: array[0..FD_SETSIZE-1, SocketHandle]
 
-  Timeval* = object
-    tv_sec*, tv_usec*: int32
-
   AddrInfo* = object
     ai_flags*: cint         ## Input flags.
     ai_family*: cint        ## Address family of socket.
@@ -531,6 +528,14 @@ type
 
   SockLen* = cuint
 
+when defined(cpp):
+  type
+    Timeval* {.importc: "timeval", header: "<time.h>".} = object
+      tv_sec*, tv_usec*: int32
+else:
+  type
+    Timeval* = object
+      tv_sec*, tv_usec*: int32
 
 var
   SOMAXCONN* {.importc, header: "winsock2.h".}: cint
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 2f072d5c7..9ee73a44d 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -38,7 +38,11 @@ when useWinVersion:
 
   from winlean import SocketHandle
 else:
-  const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+  when defined(osx):
+    # todo: find a better workaround for #10281 (caused by #10230)
+    const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)"
+  else:
+    const versions = "(.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.46|.45|.44|.43|.41|.39|.38|.10|)"
 
   when defined(macosx):
     const
diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim
index e0afe6b94..db2095128 100644
--- a/nimdoc/tester.nim
+++ b/nimdoc/tester.nim
@@ -28,5 +28,5 @@ proc test(dir: string; fixup = false) =
       echo "SUCCESS: files identical: ", produced
   removeDir(dir / "htmldocs")
 
-test("nimdoc/testproject", false)
+test("nimdoc/testproject", defined(fixup))
 if failures > 0: quit($failures & " failures occurred.")
diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
index 2c89acce4..9ba3c24e1 100644
--- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html
+++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
@@ -1238,7 +1238,12 @@ function main() {
     </select>
   </div>
   <ul class="simple simple-toc" id="toc-list">
-<li>
+<li><a class="reference" id="this-is-now-a-header_toc" href="#this-is-now-a-header">This is now a header</a></li>
+<ul class="simple"><li><a class="reference" id="this-is-now-a-header-next-header_toc" href="#this-is-now-a-header-next-header">Next header</a></li>
+<ul class="simple"><li><a class="reference" id="next-header-and-so-on_toc" href="#next-header-and-so-on">And so on</a></li>
+</ul></ul><li><a class="reference" id="more-headers_toc" href="#more-headers">More headers</a></li>
+<ul class="simple"><li><a class="reference" id="more-headers-up-to-level-6_toc" href="#more-headers-up-to-level-6">Up to level 6</a></li>
+</ul><li>
   <a class="reference reference-toplevel" href="#7" id="57">Types</a>
   <ul class="simple simple-toc-section">
       <li><a class="reference" href="#SomeType"
@@ -1271,7 +1276,19 @@ function main() {
   </div>
   <div class="nine columns" id="content">
   <div id="tocRoot"></div>
-  <p class="module-desc"></p>
+  <p class="module-desc">
+<h1><a class="toc-backref" id="this-is-now-a-header" href="#this-is-now-a-header">This is now a header</a></h1>
+<h2><a class="toc-backref" id="this-is-now-a-header-next-header" href="#this-is-now-a-header-next-header">Next header</a></h2>
+<h3><a class="toc-backref" id="next-header-and-so-on" href="#next-header-and-so-on">And so on</a></h3>
+<h1><a class="toc-backref" id="more-headers" href="#more-headers">More headers</a></h1>
+<h6><a class="toc-backref" id="more-headers-up-to-level-6" href="#more-headers-up-to-level-6">Up to level 6</a></h6><ol class="simple"><li>An enumeration</li>
+<li>Second idea here.</li>
+</ol>
+<p>More text.</p>
+<ol class="simple"><li>Other case value</li>
+<li>Second case.</li>
+</ol>
+</p>
   <div class="section" id="7">
 <h1><a class="toc-backref" href="#7">Types</a></h1>
 <dl class="item">
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html
index 2e23a64d5..dd1541a56 100644
--- a/nimdoc/testproject/expected/testproject.html
+++ b/nimdoc/testproject/expected/testproject.html
@@ -1369,7 +1369,7 @@ The enum B.
 <a id="someFunc,"></a>
 <dt><pre><span class="Keyword">func</span> <span class="Identifier">someFunc</span><span class="Other">(</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
 <dd>
-My someFunc.
+My someFunc. Stuff in <tt class="docutils literal"><span class="pre">quotes</span></tt> here. <a class="reference external" href="https://nim-lang.org">Some link</a>
 
 </dd>
 
diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html
index 652077be8..95c667acc 100644
--- a/nimdoc/testproject/expected/theindex.html
+++ b/nimdoc/testproject/expected/theindex.html
@@ -1220,7 +1220,7 @@ function main() {
 <div class="document" id="documentId">
   <div class="container">
     <h1 class="title">Index</h1>
-    Modules: <a href="subdir/subdir_b/utils.html">subdir/subdir_b/utils</a>, <a href="testproject.html">testproject</a>.<br/><p /><h2>API symbols</h2>
+    Modules: <a href="testproject.html">testproject</a>, <a href="utils.html">utils</a>.<br/><p /><h2>API symbols</h2>
 <dl><dt><a name="A" href="#A"><span>A:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
           data-doc-search-tag="testproject: A" href="testproject.html#A">testproject: A</a></li>
diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim
index e2ec80dc2..0576f194f 100644
--- a/nimdoc/testproject/subdir/subdir_b/utils.nim
+++ b/nimdoc/testproject/subdir/subdir_b/utils.nim
@@ -1,3 +1,25 @@
+##[
+
+# This is now a header
+
+## Next header
+
+### And so on
+
+# More headers
+
+###### Up to level 6
+
+
+#. An enumeration
+#. Second idea here.
+
+More text.
+
+1. Other case value
+2. Second case.
+
+]##
 
 type
   SomeType* = enum
@@ -23,4 +45,6 @@ template bEnum*(): untyped =
 
   func someFunc*() =
     ## My someFunc.
+    ## Stuff in `quotes` here.
+    ## [Some link](https://nim-lang.org)
     discard
diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim
index 628bc163e..c6c558f92 100644
--- a/nimpretty/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -25,11 +25,10 @@ const
 Usage:
   nimpretty [options] file.nim
 Options:
-  --backup:on|off     create a backup file before overwritting (default: ON)
-  --output:file       set the output file (default: overwrite the .nim file)
-  --indent:N          set the number of spaces that is used for indentation
-  --version           show the version
-  --help              show this help
+  --output:file         set the output file (default: overwrite the input file)
+  --indent:N[=2]        set the number of spaces that is used for indentation
+  --version             show the version
+  --help                show this help
 """
 
 proc writeHelp() =
@@ -62,7 +61,11 @@ proc prettyPrint(infile, outfile: string, opt: PrettyOptions) =
 
 proc main =
   var infile, outfile: string
-  var backup = true
+  var backup = false
+    # when `on`, create a backup file of input in case
+    # `prettyPrint` could over-write it (note that the backup may happen even
+    # if input is not actually over-written, when nimpretty is a noop).
+    # --backup was un-documented (rely on git instead).
   var opt: PrettyOptions
   for kind, key, val in getopt():
     case kind
@@ -79,9 +82,14 @@ proc main =
     of cmdEnd: assert(false) # cannot happen
   if infile.len == 0:
     quit "[Error] no input file."
+  if outfile.len == 0:
+    outfile = infile
+  if not existsFile(outfile) or not sameFile(infile, outfile):
+    backup = false # no backup needed since won't be over-written
   if backup:
-    os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup"))
-  if outfile.len == 0: outfile = infile
+    let infileBackup = infile & ".backup" # works with .nim or .nims
+    echo "writing backup " & infile & " > " & infileBackup
+    os.copyFile(source = infile, dest = infileBackup)
   prettyPrint(infile, outfile, opt)
 
 main()
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index c8c6101d7..1b5e8326d 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -35,6 +35,7 @@ Usage:
   nimsuggest [options] projectfile.nim
 
 Options:
+  --autobind              automatically binds into a free port
   --port:PORT             port, by default 6000
   --address:HOST          binds to that address, by default ""
   --stdin                 read commands from stdin and write results to
@@ -50,6 +51,8 @@ Options:
 
 The server then listens to the connection and takes line-based commands.
 
+If --autobind is used, the binded port number will be printed to stdout.
+
 In addition, all command line options of Nim that do not affect code generation
 are supported.
 """
@@ -68,6 +71,7 @@ var
   gEmitEof: bool # whether we write '!EOF!' dummy lines
   gLogging = defined(logging)
   gRefresh: bool
+  gAutoBind = false
 
   requests: Channel[string]
   results: Channel[Suggest]
@@ -99,7 +103,7 @@ type
 proc parseQuoted(cmd: string; outp: var string; start: int): int =
   var i = start
   i += skipWhitespace(cmd, i)
-  if cmd[i] == '"':
+  if i < cmd.len and cmd[i] == '"':
     i += parseUntil(cmd, outp, '"', i+1)+2
   else:
     i += parseUntil(cmd, outp, seps, i)
@@ -306,9 +310,15 @@ proc replCmdline(x: ThreadParams) {.thread.} =
 
 proc replTcp(x: ThreadParams) {.thread.} =
   var server = newSocket()
-  server.bindAddr(x.port, x.address)
+  if gAutoBind:
+    let port = server.connectToNextFreePort(x.address)
+    server.listen()
+    echo port
+    stdout.flushFile()
+  else:
+    server.bindAddr(x.port, x.address)
+    server.listen()
   var inp = "".TaintedString
-  server.listen()
   while true:
     var stdoutSocket = newSocket()
     accept(server, stdoutSocket)
@@ -428,7 +438,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   var dirtyfile = ""
   var orig = ""
   i = parseQuoted(cmd, orig, i)
-  if cmd[i] == ';':
+  if i < cmd.len and cmd[i] == ';':
     i = parseQuoted(cmd, dirtyfile, i+1)
   i += skipWhile(cmd, seps, i)
   var line = -1
@@ -544,6 +554,9 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
       of "help", "h":
         stdout.writeline(Usage)
         quit()
+      of "autobind":
+        gMode = mtcp
+        gAutoBind = true
       of "port":
         gPort = parseInt(p.val).Port
         gMode = mtcp
diff --git a/nimsuggest/tests/taccent_highlight.nim b/nimsuggest/tests/taccent_highlight.nim
new file mode 100644
index 000000000..2f03d7c86
--- /dev/null
+++ b/nimsuggest/tests/taccent_highlight.nim
@@ -0,0 +1,8 @@
+proc `$$$`#[!]#
+
+discard """
+disabled:true
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skProc;;1;;6;;3
+"""
diff --git a/nimsuggest/tests/ttype_highlight.nim b/nimsuggest/tests/ttype_highlight.nim
new file mode 100644
index 000000000..e4189a015
--- /dev/null
+++ b/nimsuggest/tests/ttype_highlight.nim
@@ -0,0 +1,28 @@
+type
+  TypeA = int
+  TypeB* = int
+  TypeC {.unchecked.} = array[1, int]
+  TypeD[T] = T
+  TypeE* {.unchecked.} = array[0, int]#[!]#
+
+discard """
+disabled:true
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skType;;2;;2;;5
+highlight;;skType;;3;;2;;5
+highlight;;skType;;4;;2;;5
+highlight;;skType;;5;;2;;5
+highlight;;skType;;6;;2;;5
+highlight;;skType;;2;;10;;3
+highlight;;skType;;3;;11;;3
+highlight;;skType;;4;;24;;5
+highlight;;skType;;4;;33;;3
+highlight;;skType;;5;;13;;1
+highlight;;skType;;6;;25;;5
+highlight;;skType;;6;;34;;3
+highlight;;skType;;2;;10;;3
+highlight;;skType;;3;;11;;3
+highlight;;skType;;4;;33;;3
+highlight;;skType;;6;;34;;3
+"""
diff --git a/readme.md b/readme.md
index c333611e2..114086eb6 100644
--- a/readme.md
+++ b/readme.md
@@ -191,7 +191,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim.
 
 Please read the [copying.txt](copying.txt) file for more details.
 
-Copyright © 2006-2018 Andreas Rumpf, all rights reserved.
+Copyright © 2006-2019 Andreas Rumpf, all rights reserved.
 
 [nim-site]: https://nim-lang.org
 [nim-forum]: https://forum.nim-lang.org
diff --git a/testament/categories.nim b/testament/categories.nim
index a72602217..e2b5f0a78 100644
--- a/testament/categories.nim
+++ b/testament/categories.nim
@@ -97,7 +97,7 @@ proc compileRodFiles(r: var TResults, cat: Category, options: string) =
 
 proc flagTests(r: var TResults, cat: Category, options: string) =
   # --genscript
-  const filename = "tests"/"flags"/"tgenscript"
+  const filename = testsDir/"flags"/"tgenscript"
   const genopts = " --genscript"
   let nimcache = nimcacheDir(filename, genopts, targetC)
   testSpec r, makeTest(filename, genopts, cat)
@@ -352,13 +352,13 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     "8b5d28e985c0542163927d253a3e4fc9",
     "783299b98179cc725f9c46b5e3b5381f",
     "1a2b3fba1187c68d6a9bfa66854f3318",
-    "80f9c3e594a798225046e8a42e990daf"
+    "391ff57b38d9ea6f3eeb3fe69ab539d3"
   ]
 
   for i, test in tests:
-    let filename = "tests" / test.addFileExt("nim")
+    let filename = testsDir / test.addFileExt("nim")
     let testHash = getMD5(readFile(filename).string)
-    doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed."
+    doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed: " & $(i: i, testHash: testHash, refHash: refHashes[i])
   # Run the tests.
   for testfile in tests:
     test "tests/" & testfile & ".nim"
@@ -402,14 +402,39 @@ proc compileExample(r: var TResults, pattern, options: string, cat: Category) =
     testSpec r, test
 
 proc testStdlib(r: var TResults, pattern, options: string, cat: Category) =
-  for testFile in os.walkFiles(pattern):
-    let name = extractFilename(testFile)
-    if name notin disabledFiles:
-      let contents = readFile(testFile).string
-      var testObj = makeTest(testFile, options, cat)
-      if "when isMainModule" notin contents:
-        testObj.spec.action = actionCompile
-      testSpec r, testObj
+  var files: seq[string]
+
+  proc isValid(file: string): bool =
+    for dir in parentDirs(file, inclusive = false):
+      if dir.lastPathPart in ["includes", "nimcache"]:
+        # eg: lib/pure/includes/osenv.nim gives: Error: This is an include file for os.nim!
+        return false
+    let name = extractFilename(file)
+    if name.splitFile.ext != ".nim": return false
+    for namei in disabledFiles:
+      # because of `LockFreeHash.nim` which has case
+      if namei.cmpPaths(name) == 0: return false
+    return true
+
+  for testFile in os.walkDirRec(pattern):
+    if isValid(testFile):
+      files.add testFile
+
+  files.sort # reproducible order
+  for testFile in files:
+    let contents = readFile(testFile).string
+    var testObj = makeTest(testFile, options, cat)
+    #[
+    todo:
+    this logic is fragile:
+    false positives (if appears in a comment), or false negatives, eg
+    `when defined(osx) and isMainModule`.
+    Instead of fixing this, see https://github.com/nim-lang/Nim/issues/10045
+    for a much better way.
+    ]#
+    if "when isMainModule" notin contents:
+      testObj.spec.action = actionCompile
+    testSpec r, testObj
 
 # ----------------------------- nimble ----------------------------------------
 type
@@ -506,7 +531,7 @@ proc `&.?`(a, b: string): string =
   result = if b.startswith(a): b else: a & b
 
 proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
-  let test = "tests" & DirSep &.? cat.string / test
+  let test = testsDir &.? cat.string / test
   let target = if cat.string.normalize == "js": targetJS else: targetC
   if existsFile(test):
     testSpec r, makeTest(test, options, cat), {target}
@@ -517,7 +542,9 @@ proc isJoinableSpec(spec: TSpec): bool =
   result = not spec.sortoutput and
     spec.action == actionRun and
     not fileExists(spec.file.changeFileExt("cfg")) and
+    not fileExists(spec.file.changeFileExt("nims")) and
     not fileExists(parentDir(spec.file) / "nim.cfg") and
+    not fileExists(parentDir(spec.file) / "config.nims") and
     spec.cmd.len == 0 and
     spec.err != reDisabled and
     not spec.unjoinable and
@@ -529,41 +556,75 @@ proc isJoinableSpec(spec: TSpec): bool =
     (spec.targets == {} or spec.targets == {targetC})
 
 proc norm(s: var string) =
+  # equivalent of s/\n+/\n/g (could use a single pass over input if needed)
   while true:
     let tmp = s.replace("\n\n", "\n")
     if tmp == s: break
     s = tmp
   s = s.strip
 
+proc isTestFile*(file: string): bool =
+  let (_, name, ext) = splitFile(file)
+  result = ext == ".nim" and name.startsWith("t")
+
+proc quoted(a: string): string =
+  # todo: consider moving to system.nim
+  result.addQuoted(a)
+
 proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
   ## returs a list of tests that have problems
   var specs: seq[TSpec] = @[]
-
   for kind, dir in walkDir(testsDir):
     assert testsDir.startsWith(testsDir)
     let cat = dir[testsDir.len .. ^1]
     if kind == pcDir and cat notin specialCategories:
-      for file in os.walkFiles(testsDir / cat / "t*.nim"):
+      for file in walkDirRec(testsDir / cat):
+        if not isTestFile(file): continue
         let spec = parseSpec(file)
         if isJoinableSpec(spec):
           specs.add spec
 
+  proc cmp(a: TSpec, b:TSpec): auto = cmp(a.file, b.file)
+  sort(specs, cmp=cmp) # reproducible order
   echo "joinable specs: ", specs.len
 
+  if simulate:
+    var s = "runJoinedTest: "
+    for a in specs: s.add a.file & " "
+    echo s
+    return
+
   var megatest: string
-  for runSpec in specs:
-    megatest.add "import r\""
-    megatest.add runSpec.file
-    megatest.add "\"\n"
+  #[
+  TODO(minor):
+  get from Nim cmd
+  put outputGotten.txt, outputGotten.txt, megatest.nim there too
+  delete upon completion, maybe
+  ]#
+  var outDir = nimcacheDir(testsDir / "megatest", "", targetC)
+  const marker = "megatest:processing: "
+
+  for i, runSpec in specs:
+    let file = runSpec.file
+    let file2 = outDir / ("megatest_" & $i & ".nim")
+    # `include` didn't work with `trecmod2.nim`, so using `import`
+    let code = "echo \"" & marker & "\", " & quoted(file) & "\n"
+    createDir(file2.parentDir)
+    writeFile(file2, code)
+    megatest.add "import " & quoted(file2) & "\n"
+    megatest.add "import " & quoted(file) & "\n"
 
   writeFile("megatest.nim", megatest)
 
-  const args = ["c", "-d:testing", "--listCmd", "megatest.nim"]
-  var (buf, exitCode) = execCmdEx2(command = "nim", args = args, options = {poStdErrToStdOut, poUsePath}, input = "")
+  let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "megatest.nim"]
+  proc onStdout(line: string) = echo line
+  var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, options = {poStdErrToStdOut, poUsePath}, input = "",
+    onStdout = if verboseMegatest: onStdout else: nil)
   if exitCode != 0:
     echo buf
     quit("megatest compilation failed")
 
+  # Could also use onStdout here.
   (buf, exitCode) = execCmdEx("./megatest")
   if exitCode != 0:
     echo buf
@@ -573,6 +634,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
   writeFile("outputGotten.txt", buf)
   var outputExpected = ""
   for i, runSpec in specs:
+    outputExpected.add marker & runSpec.file & "\n"
     outputExpected.add runSpec.output.strip
     outputExpected.add '\n'
   norm outputExpected
@@ -581,6 +643,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
     writeFile("outputExpected.txt", outputExpected)
     discard execShellCmd("diff -uNdr outputExpected.txt outputGotten.txt")
     echo "output different!"
+    # outputGotten.txt, outputExpected.txt not removed on purpose for debugging.
     quit 1
   else:
     echo "output OK"
@@ -623,8 +686,8 @@ proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
   of "async":
     asyncTests r, cat, options
   of "lib":
-    testStdlib(r, "lib/pure/*.nim", options, cat)
-    testStdlib(r, "lib/packages/docutils/highlite", options, cat)
+    testStdlib(r, "lib/pure/", options, cat)
+    testStdlib(r, "lib/packages/docutils/", options, cat)
   of "examples":
     compileExample(r, "examples/*.nim", options, cat)
     compileExample(r, "examples/gtk/*.nim", options, cat)
@@ -644,7 +707,12 @@ proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
     runJoinedTest(r, cat, testsDir)
   else:
     var testsRun = 0
-    for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"):
+    var files: seq[string]
+    for file in walkDirRec(testsDir &.? cat.string):
+      if isTestFile(file): files.add file
+    files.sort # give reproducible order
+
+    for i, name in files:
       var test = makeTest(name, options, cat)
       if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories:
         discard "run the test"
diff --git a/testament/htmlgen.nim b/testament/htmlgen.nim
index 4a10fe00c..641c1c32c 100644
--- a/testament/htmlgen.nim
+++ b/testament/htmlgen.nim
@@ -11,7 +11,7 @@
 
 import cgi, backend, strutils, json, os, tables, times
 
-import "testamenthtml.templ"
+import "testamenthtml.nimf"
 
 proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) =
   let
@@ -35,12 +35,12 @@ proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) =
     bgCtxClass = "success"
     resultSign = "ok"
     resultDescription = "PASS"
-  of "reIgnored":
+  of "reDisabled", "reJoined":
     panelCtxClass = "info"
     textCtxClass = "info"
     bgCtxClass = "info"
     resultSign = "question"
-    resultDescription = "SKIP"
+    resultDescription = if result != "reJoined": "SKIP" else: "JOINED"
   else:
     panelCtxClass = "danger"
     textCtxClass = "danger"
@@ -78,7 +78,8 @@ proc allTestResults(onlyFailing = false): AllTests =
         let state = elem["result"].str
         inc result.totalCount
         if state.contains("reSuccess"): inc result.successCount
-        elif state.contains("reIgnored"): inc result.ignoredCount
+        elif state.contains("reDisabled") or state.contains("reJoined"):
+          inc result.ignoredCount
         if not onlyFailing or not(state.contains("reSuccess")):
           result.data.add elem
   result.successPercentage = 100 *
diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim
new file mode 100644
index 000000000..3c8b2338c
--- /dev/null
+++ b/testament/lib/stdtest/specialpaths.nim
@@ -0,0 +1,31 @@
+#[
+todo: move findNimStdLibCompileTime, findNimStdLib here
+]#
+
+import os
+
+# Note: all the const paths defined here are known at compile time and valid
+# so long Nim repo isn't relocated after compilation.
+# This means the binaries they produce will embed hardcoded paths, which
+# isn't appropriate for some applications that need to be relocatable.
+
+const sourcePath = currentSourcePath()
+  # robust way to derive other paths here
+  # We don't depend on PATH so this is robust to having multiple nim
+  # binaries
+
+const nimRootDir* = sourcePath.parentDir.parentDir.parentDir.parentDir
+  ## root of Nim repo
+
+const stdlibDir* = nimRootDir / "lib"
+  # todo: make nimeval.findNimStdLibCompileTime use this
+
+const systemPath* = stdlibDir / "system.nim"
+
+const buildDir* = nimRootDir / "build"
+  ## refs #10268: all testament generated files should go here to avoid
+  ## polluting .gitignore
+
+static:
+  # sanity check
+  doAssert fileExists(systemPath)
diff --git a/testament/specs.nim b/testament/specs.nim
index df12db543..22ad7d2f8 100644
--- a/testament/specs.nim
+++ b/testament/specs.nim
@@ -9,7 +9,7 @@
 
 import parseutils, strutils, os, osproc, streams, parsecfg
 
-var compilerPrefix* = "compiler" / "nim"
+var compilerPrefix* = "compiler" / "nim"  ## built via ./koch tests
 
 let isTravis* = existsEnv("TRAVIS")
 let isAppVeyor* = existsEnv("APPVEYOR")
diff --git a/testament/testamenthtml.templ b/testament/testamenthtml.nimf
index 9190f370e..9190f370e 100644
--- a/testament/testamenthtml.templ
+++ b/testament/testamenthtml.nimf
diff --git a/testament/tester.nim b/testament/tester.nim
index 48e9f1913..ad0d22742 100644
--- a/testament/tester.nim
+++ b/testament/tester.nim
@@ -16,8 +16,12 @@ import
 
 var useColors = true
 var backendLogging = true
+var simulate = false
+var verboseMegatest = false # very verbose but can be useful
+var verboseCommands = false
 
 const
+  testsDir = "tests" & DirSep
   resultsFile = "testresults.html"
   #jsonFile = "testresults.json" # not used
   Usage = """Usage:
@@ -33,6 +37,9 @@ Arguments:
   arguments are passed to the compiler
 Options:
   --print                   also print results to the console
+  --verboseMegatest         log to stdout megatetest compilation
+  --verboseCommands         log to stdout info about commands being run
+  --simulate                see what tests would be run but don't run them (for debugging)
   --failing                 only show failing/ignored tests
   --targets:"c c++ js objc" run tests for specified targets (default: all)
   --nim:path                use a particular nim executable (default: compiler/nim)
@@ -68,7 +75,7 @@ let
   pegSuccess = peg"'Hint: operation successful'.*"
   pegOfInterest = pegLineError / pegOtherError
 
-var targets = {low(TTarget)..high(TTarget)}
+var gTargets = {low(TTarget)..high(TTarget)}
 
 proc normalizeMsg(s: string): string =
   result = newStringOfCap(s.len+1)
@@ -81,7 +88,7 @@ proc getFileDir(filename: string): string =
   if not result.isAbsolute():
     result = getCurrentDir() / result
 
-proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string): tuple[
+proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string, onStdout: proc(line: string) = nil): tuple[
                 output: TaintedString,
                 exitCode: int] {.tags:
                 [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
@@ -99,14 +106,21 @@ proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOp
   var line = newStringOfCap(120).TaintedString
   while true:
     if outp.readLine(line):
-      result[0].string.add(line.string)
-      result[0].string.add("\n")
+      result.output.string.add(line.string)
+      result.output.string.add("\n")
+      if onStdout != nil: onStdout(line.string)
     else:
-      result[1] = peekExitCode(p)
-      if result[1] != -1: break
+      result.exitCode = peekExitCode(p)
+      if result.exitCode != -1: break
   close(p)
 
-
+  if verboseCommands:
+    var command2 = command
+    if args.len > 0: command2.add " " & args.quoteShellCommand
+    echo (msg: "execCmdEx2",
+      command: command2,
+      options: options,
+      exitCode: result.exitCode)
 
 proc nimcacheDir(filename, options: string, target: TTarget): string =
   ## Give each test a private nimcache dir so they don't clobber each other's.
@@ -219,7 +233,11 @@ proc `$`(x: TResults): string =
 
 proc addResult(r: var TResults, test: TTest, target: TTarget,
                expected, given: string, success: TResultEnum) =
-  let name = test.name.extractFilename & " " & $target & test.options
+  # test.name is easier to find than test.name.extractFilename
+  # A bit hacky but simple and works with tests/testament/tshouldfail.nim
+  var name = test.name.replace(DirSep, '/')
+  name.add " " & $target & test.options
+
   let duration = epochTime() - test.startTime
   let durationStr = duration.formatFloat(ffDecimal, precision = 8).align(11)
   if backendLogging:
@@ -372,6 +390,17 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
       expected.targets = {targetC}
   for target in expected.targets:
     inc(r.total)
+    if target notin gTargets:
+      r.addResult(test, target, "", "", reDisabled)
+      inc(r.skipped)
+      continue
+
+    if simulate:
+      var count {.global.} = 0
+      count.inc
+      echo "testSpec count: ", count, " expected: ", expected
+      continue
+
     case expected.action
     of actionCompile:
       var given = callCompiler(expected.getCmd, test.name, test.options, target,
@@ -473,19 +502,33 @@ proc makeTest(test, options: string, cat: Category): TTest =
   result.spec = parseSpec(addFileExt(test, ".nim"))
   result.startTime = epochTime()
 
+# TODO: fix these files
+const disabledFilesDefault = @[
+  "LockFreeHash.nim",
+  "sharedstrings.nim",
+  "tableimpl.nim",
+
+  # Error: undeclared identifier: 'hasThreadSupport'
+  "ioselectors_epoll.nim",
+  "ioselectors_kqueue.nim",
+  "ioselectors_poll.nim",
+
+  # Error: undeclared identifier: 'Timeval'
+  "ioselectors_select.nim",
+]
+
 when defined(windows):
   const
     # array of modules disabled from compilation test of stdlib.
-    disabledFiles = ["coro.nim"]
+    disabledFiles = disabledFilesDefault & @["coro.nim"]
 else:
   const
     # array of modules disabled from compilation test of stdlib.
-    disabledFiles = ["-"]
+    # TODO: why the ["-"]? (previous code should've prob used seq[string] = @[] instead)
+    disabledFiles = disabledFilesDefault & @["-"]
 
 include categories
 
-const testsDir = "tests" & DirSep
-
 proc main() =
   os.putenv "NIMTEST_COLOR", "never"
   os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES"
@@ -500,11 +543,13 @@ proc main() =
   while p.kind == cmdLongoption:
     case p.key.string.normalize
     of "print", "verbose": optPrintResults = true
+    of "verbosemegatest": verboseMegatest = true
+    of "verbosecommands": verboseCommands = true
     of "failing": optFailing = true
     of "pedantic": discard "now always enabled"
     of "targets":
       targetsStr = p.val.string
-      targets = parseTargets(targetsStr)
+      gTargets = parseTargets(targetsStr)
     of "nim":
       compilerPrefix = addFileExt(p.val.string, ExeExt)
     of "directory":
@@ -517,6 +562,8 @@ proc main() =
         useColors = false
       else:
         quit Usage
+    of "simulate":
+      simulate = true
     of "backendlogging":
       case p.val.string:
       of "on":
@@ -543,16 +590,28 @@ proc main() =
 
     myself &= " " & quoteShell("--nim:" & compilerPrefix)
 
-    var cmds: seq[string] = @[]
+    var cats: seq[string]
     let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
     for kind, dir in walkDir(testsDir):
       assert testsDir.startsWith(testsDir)
       let cat = dir[testsDir.len .. ^1]
       if kind == pcDir and cat notin ["testdata", "nimcache"]:
-        cmds.add(myself & " pcat " & quoteShell(cat) & rest)
-    for cat in AdditionalCategories:
+        cats.add cat
+    cats.add AdditionalCategories
+
+    var cmds: seq[string]
+    for cat in cats:
       cmds.add(myself & " pcat " & quoteShell(cat) & rest)
-    quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams})
+
+    proc progressStatus(idx: int) =
+      echo "progress[all]: i: " & $idx & " / " & $cats.len & " cat: " & cats[idx]
+
+    if simulate:
+      for i, cati in cats:
+        progressStatus(i)
+        processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false)
+    else:
+      quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
   of "c", "cat", "category":
     var cat = Category(p.key)
     p.next
@@ -565,10 +624,12 @@ proc main() =
     p.next
     processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false)
   of "r", "run":
-    let (dir, file) = splitPath(p.key.string)
-    let (_, subdir) = splitPath(dir)
-    var cat = Category(subdir)
-    processSingleTest(r, cat, p.cmdLineRest.string, file)
+    # at least one directory is required in the path, to use as a category name
+    let pathParts = split(p.key.string, {DirSep, AltSep})
+    # "stdlib/nre/captures.nim" -> "stdlib" + "nre/captures.nim"
+    let cat = Category(pathParts[0])
+    let subPath = joinPath(pathParts[1..^1])
+    processSingleTest(r, cat, p.cmdLineRest.string, subPath)
   of "html":
     generateHtml(resultsFile, optFailing)
   else:
diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim
index 2a371b788..f7c1dbf7f 100644
--- a/tests/array/tarray.nim
+++ b/tests/array/tarray.nim
@@ -18,7 +18,7 @@ paper
 @[2, 3, 4]321
 9.0 4.0
 3
-@[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)]
+@[(1, 2), (3, 5)]
 2
 @["a", "new one", "c"]
 @[1, 2, 3]
diff --git a/tests/async/tasyncfilewrite.nim b/tests/async/tasyncfilewrite.nim
index 373b93301..3baf2bbc6 100644
--- a/tests/async/tasyncfilewrite.nim
+++ b/tests/async/tasyncfilewrite.nim
@@ -9,7 +9,6 @@ import os, asyncfile, asyncdispatch
 const F = "test_async.txt"
 
 removeFile(F)
-defer: removeFile(F)
 let f = openAsync(F, fmWrite)
 var futs = newSeq[Future[void]]()
 for i in 1..3:
@@ -17,4 +16,4 @@ for i in 1..3:
 waitFor(all(futs))
 f.close()
 echo readFile(F)
-
+removeFile(F)
diff --git a/tests/async/tasynctry.nim b/tests/async/tasynctry.nim
index b13c57951..a7cb5223d 100644
--- a/tests/async/tasynctry.nim
+++ b/tests/async/tasynctry.nim
@@ -6,6 +6,7 @@ Multiple idents in except
 Multiple except branches
 Multiple except branches 2
 '''
+targets: "c"
 """
 import asyncdispatch, strutils
 
diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim
index 64c5d6c26..6763eb5a2 100644
--- a/tests/async/twinasyncrw.nim
+++ b/tests/async/twinasyncrw.nim
@@ -46,7 +46,7 @@ when defined(windows):
           success = false
       it = it.ai_next
 
-    dealloc(aiList)
+    freeAddrInfo(aiList)
     if not success:
       retFuture.fail(newException(OSError, osErrorMsg(lastError)))
     return retFuture
diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim
index f16b323de..c06926805 100644
--- a/tests/bind/tnicerrorforsymchoice.nim
+++ b/tests/bind/tnicerrorforsymchoice.nim
@@ -1,10 +1,15 @@
 discard """
   errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}>"
-  line: 18
+  line: 23
 """
 
+# Fake ScgiState objects, from now-deprecated scgi module
+type
+  ScgiState* = object of RootObj ## SCGI state object
+  AsyncScgiState* = object of RootObj ## SCGI state object
+
 #bug #442
-import scgi, sockets, asyncio, strtabs
+import sockets, asyncio, strtabs
 proc handleSCGIRequest[TScgi: ScgiState | AsyncScgiState](s: TScgi) =
   discard
 proc handleSCGIRequest(client: AsyncSocket, headers: StringTableRef,
diff --git a/tests/ccgbugs/t5701.nim b/tests/ccgbugs/t5701.nim
index e69acbf31..ee6e48498 100644
--- a/tests/ccgbugs/t5701.nim
+++ b/tests/ccgbugs/t5701.nim
@@ -1,7 +1,7 @@
 discard """
-  output: '''(Field0: 1, Field1: 1)
-(Field0: 2, Field1: 2)
-(Field0: 3, Field1: 3)
+  output: '''(1, 1)
+(2, 2)
+(3, 3)
 '''
 """
 
diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim
index 2a867bc3b..74fbae303 100644
--- a/tests/ccgbugs/tforward_decl_only.nim
+++ b/tests/ccgbugs/tforward_decl_only.nim
@@ -1,5 +1,7 @@
 discard """
 ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')"
+ccodecheck: "\\i !@('mymoduleInit')"
+ccodecheck: "\\i @('mymoduleDatInit')"
 output: "hello"
 """
 
diff --git a/tests/collections/tcollections_to_string.nim b/tests/collections/tcollections_to_string.nim
index 0c4f1e91c..686b9916b 100644
--- a/tests/collections/tcollections_to_string.nim
+++ b/tests/collections/tcollections_to_string.nim
@@ -9,9 +9,9 @@ import lists
 import critbits
 
 # Tests for tuples
-doAssert $(1, 2, 3) == "(Field0: 1, Field1: 2, Field2: 3)"
-doAssert $("1", "2", "3") == """(Field0: "1", Field1: "2", Field2: "3")"""
-doAssert $('1', '2', '3') == """(Field0: '1', Field1: '2', Field2: '3')"""
+doAssert $(1, 2, 3) == "(1, 2, 3)"
+doAssert $("1", "2", "3") == """("1", "2", "3")"""
+doAssert $('1', '2', '3') == """('1', '2', '3')"""
 
 # Tests for seqs
 doAssert $(@[1, 2, 3]) == "@[1, 2, 3]"
diff --git a/tests/compiler/nim.cfg b/tests/compiler/nim.cfg
new file mode 100644
index 000000000..6f49473aa
--- /dev/null
+++ b/tests/compiler/nim.cfg
@@ -0,0 +1,7 @@
+# note: consider moving tests/compilerapi/ to tests/compiler/ since
+# that's more predictable.
+
+# note: without this, tests may succeed locally but fail on CI (it can succeed
+# locally even when compiling via `./bin/nim` because `$HOME/.nimble` is being
+# used).
+--path:"../../" # so we can `import compiler/foo` in this dir
diff --git a/tests/compiler/tasciitables.nim b/tests/compiler/tasciitables.nim
new file mode 100644
index 000000000..0a5ee0f05
--- /dev/null
+++ b/tests/compiler/tasciitables.nim
@@ -0,0 +1,109 @@
+import compiler/unittest_light
+import compiler/asciitables
+
+import strformat
+
+proc alignTableCustom(s: string, delim = '\t', sep = ","): string =
+  for cell in parseTableCells(s, delim):
+    result.add fmt"({cell.row},{cell.col}): "
+    for i in cell.text.len..<cell.width:
+      result.add " "
+    result.add cell.text
+    if cell.col < cell.ncols-1:
+      result.add sep
+    if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
+      result.add '\n'
+
+proc testAlignTable() =
+  block: # test with variable width columns
+    var ret = ""
+    ret.add "12\t143\tbcdef\n"
+    ret.add "2\t14394852020\tbcdef\n"
+    ret.add "45342\t1\tbf\n"
+    ret.add "45342\t1\tbsadfasdfasfdasdff\n"
+    ret.add "453232323232342\t1\tbsadfasdfasfdasdff\n"
+    ret.add "45342\t1\tbf\n"
+    ret.add "45342\t1\tb afasf a ff\n"
+    ret.add "4\t1\tbf\n"
+
+    assertEquals alignTable(ret),
+      """
+12              143         bcdef             
+2               14394852020 bcdef             
+45342           1           bf                
+45342           1           bsadfasdfasfdasdff
+453232323232342 1           bsadfasdfasfdasdff
+45342           1           bf                
+45342           1           b afasf a ff      
+4               1           bf                
+"""
+
+    assertEquals alignTable(ret, fill = '.', sep = ","),
+      """
+12.............,143........,bcdef.............
+2..............,14394852020,bcdef.............
+45342..........,1..........,bf................
+45342..........,1..........,bsadfasdfasfdasdff
+453232323232342,1..........,bsadfasdfasfdasdff
+45342..........,1..........,bf................
+45342..........,1..........,b afasf a ff......
+4..............,1..........,bf................
+"""
+
+    assertEquals alignTableCustom(ret, sep = "  "),
+      """
+(0,0):              12  (0,1):         143  (0,2):              bcdef
+(1,0):               2  (1,1): 14394852020  (1,2):              bcdef
+(2,0):           45342  (2,1):           1  (2,2):                 bf
+(3,0):           45342  (3,1):           1  (3,2): bsadfasdfasfdasdff
+(4,0): 453232323232342  (4,1):           1  (4,2): bsadfasdfasfdasdff
+(5,0):           45342  (5,1):           1  (5,2):                 bf
+(6,0):           45342  (6,1):           1  (6,2):       b afasf a ff
+(7,0):               4  (7,1):           1  (7,2):                 bf
+"""
+
+  block: # test with 1 column
+    var ret = "12\nasdfa\nadf"
+    assertEquals alignTable(ret), """
+12   
+asdfa
+adf  """
+
+  block: # test with empty input
+    var ret = ""
+    assertEquals alignTable(ret), ""
+
+  block: # test with 1 row
+    var ret = "abc\tdef"
+    assertEquals alignTable(ret), """
+abc def"""
+
+  block: # test with 1 row ending in \t
+    var ret = "abc\tdef\t"
+    assertEquals alignTable(ret), """
+abc def """
+
+  block: # test with 1 row starting with \t
+    var ret = "\tabc\tdef\t"
+    assertEquals alignTable(ret), """
+ abc def """
+
+
+  block: # test with variable number of cols per row
+    var ret = """
+a1,a2,a3
+
+b1
+c1,c2
+,d1
+"""
+    assertEquals alignTableCustom(ret, delim = ',', sep = ","),
+      """
+(0,0): a1,(0,1): a2,(0,2): a3
+(1,0):   ,(1,1):   ,(1,2):   
+(2,0): b1,(2,1):   ,(2,2):   
+(3,0): c1,(3,1): c2,(3,2):   
+(4,0):   ,(4,1): d1,(4,2):   
+"""
+
+testAlignTable()
diff --git a/tests/compiler/tunittest_light.nim b/tests/compiler/tunittest_light.nim
new file mode 100644
index 000000000..422474002
--- /dev/null
+++ b/tests/compiler/tunittest_light.nim
@@ -0,0 +1,55 @@
+import compiler/unittest_light
+
+proc testAssertEquals() =
+  assertEquals("foo", "foo")
+  doAssertRaises(AssertionError):
+    assertEquals("foo", "foo ")
+
+proc testMismatch() =
+  assertEquals(1+1, 2*1)
+
+  let a = """
+  some test with space at the end of lines    
+
+  can be hard to spot differences when diffing in a terminal   
+  without this helper function
+
+"""
+
+  let b = """
+  some test with space at the end of lines    
+
+  can be hard to spot differences when diffing in a terminal  
+  without this helper function
+
+"""
+
+  let output = mismatch(a, b)
+  let expected = """
+
+lhs:{  some test with space at the end of lines    \n
+\n
+  can be hard to spot differences when diffing in a terminal   \n
+  without this helper function\n
+\n
+}
+rhs:{  some test with space at the end of lines    \n
+\n
+  can be hard to spot differences when diffing in a terminal  \n
+  without this helper function\n
+\n
+}
+lhs.len: 144 rhs.len: 143
+first mismatch index: 110
+lhs[i]: {" "}
+rhs[i]: {"\n"}
+lhs[0..<i]:{  some test with space at the end of lines    \n
+\n
+  can be hard to spot differences when diffing in a terminal  }"""
+
+  if output != expected:
+    echo output
+    doAssert false
+
+testMismatch()
+testAssertEquals()
diff --git a/tests/cpp/t10148.nim b/tests/cpp/t10148.nim
new file mode 100644
index 000000000..e8dd3098f
--- /dev/null
+++ b/tests/cpp/t10148.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''Expected successful exit'''
+  joinable: false
+"""
+
+import os
+
+proc another_proc: string =
+  ## trigger many GC allocations
+  var x = @[""]
+  for i in 0..100:
+   x.add $i
+  result = "not_existent_path"
+
+proc findlib2: string =
+  let path = getEnv("MYLIB2_DOES_NOT_EXIST_PATH")
+  let another_path = another_proc()
+  GC_fullCollect()
+
+  if path.len > 0 and dirExists(path):
+    path / "alib_does_not_matter.dll"
+  elif fileExists(another_path):
+    another_path
+  else:
+    quit("Expected successful exit", 0)
+
+proc imported_func*(a: cint): cstring {.importc, dynlib: findlib2().}
+
+echo imported_func(0)
diff --git a/tests/cpp/t10241.nim b/tests/cpp/t10241.nim
new file mode 100644
index 000000000..21d6a0f4e
--- /dev/null
+++ b/tests/cpp/t10241.nim
@@ -0,0 +1,19 @@
+discard """
+  targets: "cpp"
+  action: "compile"
+"""
+
+type
+  String* {.importcpp: "std::string", header: "string".} = object
+
+proc initString*(): String
+    {.importcpp: "std::string()", header: "string".}
+
+proc append*(this: var String, str: String): var String
+    {.importcpp: "append", header: "string", discardable.}
+
+var
+  s1 = initString()
+  s2 = initString()
+
+s1.append s2
diff --git a/tests/deprecated/tannot.nim b/tests/deprecated/tannot.nim
new file mode 100644
index 000000000..d14f6cc23
--- /dev/null
+++ b/tests/deprecated/tannot.nim
@@ -0,0 +1,9 @@
+discard """
+  nimout: '''tannot.nim(9, 1) Warning: efgh; foo1 is deprecated [Deprecated]
+tannot.nim(9, 8) Warning: abcd; foo is deprecated [Deprecated]
+'''
+"""
+
+let foo* {.deprecated: "abcd".} = 42
+var foo1* {.deprecated: "efgh".} = 42
+foo1 = foo
diff --git a/tests/deprecated/tdeprecated.nim b/tests/deprecated/tdeprecated.nim
index 955a7f6ad..920f350cc 100644
--- a/tests/deprecated/tdeprecated.nim
+++ b/tests/deprecated/tdeprecated.nim
@@ -1,9 +1,19 @@
 discard """
-  nimout: "a is deprecated [Deprecated]"
+  nimout: '''tdeprecated.nim(10, 3) Warning: a is deprecated [Deprecated]
+tdeprecated.nim(17, 11) Warning: asdf; enum 'Foo' which contains field 'a' is deprecated [Deprecated]
+'''
 """
+block:
+  var
+    a {.deprecated.}: array[0..11, int]
 
-var
-  a {.deprecated.}: array[0..11, int]
+  a[8] = 1
 
-a[8] = 1
+block t10111:
+  type
+    Foo {.deprecated: "asdf" .} = enum
+      a 
+  
+  var _ = a
+  
 
diff --git a/tests/destructor/helper.nim b/tests/destructor/helper.nim
new file mode 100644
index 000000000..466065747
--- /dev/null
+++ b/tests/destructor/helper.nim
@@ -0,0 +1,3 @@
+type
+  MyTestObject*[T] = object
+    p: ptr T
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index c9f1caf2d..09dce19ab 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -7,21 +7,28 @@ mygeneric1 constructed
 mygeneric1 destroyed
 ----
 mygeneric2 constructed
-mygeneric2 destroyed
 myobj destroyed
+mygeneric2 destroyed
 ----
 mygeneric3 constructed
 mygeneric1 destroyed
 ----
-mygeneric1 destroyed
-----
+mydistinctObj constructed
 myobj destroyed
+mygeneric2 destroyed
+------------------
 ----
 ----
 myobj destroyed
+mygeneric1 destroyed
+myobj destroyed
+myobj destroyed
+myobj destroyed
+---
+myobj destroyed
+myobj destroyed
+myobj destroyed
 '''
-  cmd: '''nim c --newruntime $file'''
-  disabled: "true"
 """
 
 type
@@ -29,6 +36,11 @@ type
     x, y: int
     p: pointer
 
+proc `=destroy`(o: var TMyObj) =
+  if o.p != nil: dealloc o.p
+  echo "myobj destroyed"
+
+type
   TMyGeneric1[T] = object
     x: T
 
@@ -36,37 +48,40 @@ type
     x: A
     y: B
 
+proc `=destroy`(o: var TMyGeneric1[int]) =
+  echo "mygeneric1 destroyed"
+
+proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
+  echo "mygeneric2 destroyed"
+
+type
   TMyGeneric3[A, B, C] = object
     x: A
     y: B
     z: C
 
-  TObjKind = enum A, B, C, D
+  TDistinctObjX = distinct TMyGeneric3[TMyObj, TMyGeneric2[int, int], int]
+  TDistinctObj = TDistinctObjX
+
+  TObjKind = enum Z, A, B, C, D
 
   TCaseObj = object
+    z: TMyGeneric3[TMyObj, float, int]
     case kind: TObjKind
+    of Z: discard
     of A:
       x: TMyGeneric1[int]
     of B, C:
       y: TMyObj
     else:
       case innerKind: TObjKind
+      of Z: discard
       of A, B, C:
         p: TMyGeneric3[int, float, string]
       of D:
         q: TMyGeneric3[TMyObj, int, int]
       r: string
 
-proc `=destroy`(o: var TMyObj) =
-  if o.p != nil: dealloc o.p
-  echo "myobj destroyed"
-
-proc `=destroy`(o: var TMyGeneric1[int]) =
-  echo "mygeneric1 destroyed"
-
-proc `=destroy`[A, B](o: var TMyGeneric2[A, B]) =
-  echo "mygeneric2 destroyed"
-
 proc open: TMyObj =
   # allow for superfluous ()
   result = (TMyObj(x: 1, y: 2, p: alloc(3)))
@@ -95,6 +110,12 @@ proc mygeneric3 =
 
   echo "mygeneric3 constructed"
 
+proc mydistinctObj =
+  var x = TMyGeneric3[TMyObj, TMyGeneric2[int, int], int](
+    x: open(), y: TMyGeneric2[int, int](x: 5, y: 15), z: 20)
+
+  echo "mydistinctObj constructed"
+
 echo "----"
 myobj()
 
@@ -107,9 +128,11 @@ mygeneric2[int](10)
 echo "----"
 mygeneric3()
 
+echo "----"
+mydistinctObj()
+
 proc caseobj =
   block:
-    echo "----"
     var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
 
   block:
@@ -121,10 +144,16 @@ proc caseobj =
     var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
                       p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
 
-  block:
-    echo "----"
-    var o4 = TCaseObj(kind: D, innerKind: D, r: "test",
-                      q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
 
+echo "------------------"
 caseobj()
 
+proc caseobj_test_sink: TCaseObj =
+  # check that lifted sink can destroy case val correctly
+  result = TCaseObj(kind: D, innerKind: D, r: "test",
+                      q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
+  result = TCaseObj(kind: B, y: open())
+
+
+echo "---"
+discard caseobj_test_sink()
\ No newline at end of file
diff --git a/tests/destructor/terror_module.nim b/tests/destructor/terror_module.nim
new file mode 100644
index 000000000..f3d7c9b26
--- /dev/null
+++ b/tests/destructor/terror_module.nim
@@ -0,0 +1,20 @@
+discard """
+joinable: false
+cmd: "nim check $file"
+errormsg: "type bound operation `=deepcopy` can be defined only in the same module with its type (MyTestObject)"
+nimout: '''
+terror_module.nim(14, 1) Error: type bound operation `=destroy` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(16, 1) Error: type bound operation `=sink` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(18, 1) Error: type bound operation `=` can be defined only in the same module with its type (MyTestObject)
+terror_module.nim(20, 1) Error: type bound operation `=deepcopy` can be defined only in the same module with its type (MyTestObject)
+'''
+"""
+import helper
+
+proc `=destroy`[T](x: var MyTestObject[T]) = discard
+
+proc `=sink`[T](x: var MyTestObject[T], y:MyTestObject[T]) = discard
+
+proc `=`[T](x: var MyTestObject[T], y: MyTestObject[T]) = discard
+
+proc `=deepcopy`[T](x: ptr MyTestObject[T]): ptr MyTestObject[T] = discard
diff --git a/tests/discard/tneedsdiscard.nim b/tests/discard/tneedsdiscard.nim
index 7d2997b3f..d9483947f 100644
--- a/tests/discard/tneedsdiscard.nim
+++ b/tests/discard/tneedsdiscard.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded; start of expression here: tneedsdiscard.nim(7, 2)'''
+  errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded; start of expression here: tneedsdiscard.nim(7, 3)'''
   line: 10
 """
 
diff --git a/tests/enum/tenumfieldpragma.nim b/tests/enum/tenumfieldpragma.nim
new file mode 100644
index 000000000..604a8f019
--- /dev/null
+++ b/tests/enum/tenumfieldpragma.nim
@@ -0,0 +1,22 @@
+discard """
+  nimout: '''tenumfieldpragma.nim(20, 10) Warning: d is deprecated [Deprecated]
+tenumfieldpragma.nim(21, 10) Warning: e is deprecated [Deprecated]
+tenumfieldpragma.nim(22, 10) Warning: f is deprecated [Deprecated]
+'''
+"""
+
+type
+  A = enum
+    a
+    b = "abc"
+    c = (10, "def")
+    d {.deprecated.}
+    e {.deprecated.} = "ghi"
+    f {.deprecated.} = (20, "jkl")
+
+var v1 = a
+var v2 = b
+var v3 = c
+var v4 = d
+var v5 = e
+var v6 = f
diff --git a/tests/deprecated/tnoannot.nim b/tests/enum/tenumfieldpragmanoannot.nim
index ac168952e..47f920827 100644
--- a/tests/deprecated/tnoannot.nim
+++ b/tests/enum/tenumfieldpragmanoannot.nim
@@ -1,7 +1,10 @@
 discard """
   errormsg: "annotation to deprecated not supported here"
-  line: 7
+  line: 8
 """
 
-var foo* {.deprecated.} = 42
-var foo1* {.deprecated: "no".} = 42
+type
+  A = enum
+    a {.deprecated: "njshd".}
+
+var v1 = a
diff --git a/tests/errmsgs/m8794.nim b/tests/errmsgs/m8794.nim
new file mode 100644
index 000000000..12e61cf54
--- /dev/null
+++ b/tests/errmsgs/m8794.nim
@@ -0,0 +1,2 @@
+type Foo3* = object
+  a1: int
diff --git a/tests/errmsgs/t8794.nim b/tests/errmsgs/t8794.nim
new file mode 100644
index 000000000..7f16a42fe
--- /dev/null
+++ b/tests/errmsgs/t8794.nim
@@ -0,0 +1,39 @@
+discard """
+  cmd: "nim check $options $file"
+  errormsg: ""
+  nimout: '''
+t8794.nim(39, 27) Error: undeclared field: 'a3' for type m8794.Foo3[declared in m8794.nim(1, 6)]
+'''
+"""
+
+
+
+
+
+
+
+
+
+
+
+
+## line 20
+
+## issue #8794
+
+import m8794
+
+when false: # pending https://github.com/nim-lang/Nim/pull/10091 add this
+  type Foo = object
+    a1: int
+
+  discard Foo().a2
+
+type Foo3b = Foo3
+var x2: Foo3b
+
+proc getFun[T](): T =
+  var a: T
+  a
+
+discard getFun[type(x2)]().a3
diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim
new file mode 100644
index 000000000..18588c87c
--- /dev/null
+++ b/tests/errmsgs/t9768.nim
@@ -0,0 +1,30 @@
+discard """
+  errmsg: "unhandled exception:"
+  file: "system.nim"
+  nimout: '''
+stack trace: (most recent call last)
+t9768.nim(28, 33)        main
+t9768.nim(23, 11)        foo1
+'''
+"""
+
+
+
+
+
+
+
+
+
+
+## line 20
+
+proc foo1(a: int): auto =
+  doAssert a < 4
+  result = a * 2
+
+proc main()=
+  static:
+    if foo1(1) > 0: discard foo1(foo1(2))
+
+main()
diff --git a/tests/errmsgs/tinteger_literals.nim b/tests/errmsgs/tinteger_literals.nim
new file mode 100644
index 000000000..98c92a227
--- /dev/null
+++ b/tests/errmsgs/tinteger_literals.nim
@@ -0,0 +1,15 @@
+discard """
+cmd: "nim check $file"
+errormsg: "number out of range: '300'u8'"
+nimout: '''
+tinteger_literals.nim(12, 9) Error: number out of range: '18446744073709551616'u64'
+tinteger_literals.nim(13, 9) Error: number out of range: '9223372036854775808'i64'
+tinteger_literals.nim(14, 9) Error: number out of range: '9223372036854775808'
+tinteger_literals.nim(15, 9) Error: number out of range: '300'u8'
+'''
+"""
+
+discard 18446744073709551616'u64 # high(uint64) + 1
+discard 9223372036854775808'i64  # high(int64) + 1
+discard 9223372036854775808      # high(int64) + 1
+discard 300'u8
\ No newline at end of file
diff --git a/tests/errmsgs/tnested_generic_instantiation.nim b/tests/errmsgs/tnested_generic_instantiation.nim
index 6aea7cbcc..77353605c 100644
--- a/tests/errmsgs/tnested_generic_instantiation.nim
+++ b/tests/errmsgs/tnested_generic_instantiation.nim
@@ -17,3 +17,9 @@ converter toWrapped[T](value: T): Wrapped[T] =
 
 let result = Plain()
 discard $result
+
+proc foo[T2](a: Wrapped[T2]) =
+  # Error: generic instantiation too nested
+  discard $a
+
+foo(result)
diff --git a/tests/errmsgs/tnested_generic_instantiation2.nim b/tests/errmsgs/tnested_generic_instantiation2.nim
new file mode 100644
index 000000000..d9bba15b0
--- /dev/null
+++ b/tests/errmsgs/tnested_generic_instantiation2.nim
@@ -0,0 +1,27 @@
+discard """
+errormsg: "generic instantiation too nested"
+"""
+
+#[
+bug #4766
+see also: tnested_generic_instantiation.nim
+]#
+
+proc toString*[T](x: T) =
+  for name, value in fieldPairs(x):
+    when compiles(toString(value)):
+      discard
+    toString(value)
+
+type
+  Plain = ref object
+    discard
+
+  Wrapped[T] = object
+    value: T
+
+converter toWrapped[T](value: T): Wrapped[T] =
+  Wrapped[T](value: value)
+
+let result = Plain()
+toString(result)
diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim
index c74fea46f..b339e06bf 100644
--- a/tests/errmsgs/treportunused.nim
+++ b/tests/errmsgs/treportunused.nim
@@ -1,12 +1,12 @@
 discard """
   nimout: '''
-treportunused.nim(19, 10) Hint: 'treportunused.s1(a: string)[declared in treportunused.nim(19, 9)]' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(19, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(26, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed]
-treportunused.nim(22, 6) Hint: 'treportunused.s4()[declared in treportunused.nim(22, 5)]' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(22, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(25, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(24, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed]
-treportunused.nim(23, 6) Hint: 'treportunused.s5(a: T)[declared in treportunused.nim(23, 5)]' is declared but not used [XDeclaredButNotUsed]
-treportunused.nim(20, 10) Hint: 'treportunused.s2()[declared in treportunused.nim(20, 9)]' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(23, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed]
+treportunused.nim(20, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(29, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(27, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed]
 treportunused.nim(21, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed]
diff --git a/tests/errmsgs/tunknown_named_parameter.nim b/tests/errmsgs/tunknown_named_parameter.nim
index b6b855136..8a3bcaf03 100644
--- a/tests/errmsgs/tunknown_named_parameter.nim
+++ b/tests/errmsgs/tunknown_named_parameter.nim
@@ -2,10 +2,6 @@ discard """
 cmd: "nim check $file"
 errormsg: "type mismatch: got <string, set[char], maxsplits: int literal(1)>"
 nimout: '''
-proc rsplit(s: string; sep: string; maxsplit: int = -1): seq[string]
-  first type mismatch at position: 2
-  required type: string
-  but expression '{':'}' is of type: set[char]
 proc rsplit(s: string; sep: char; maxsplit: int = -1): seq[string]
   first type mismatch at position: 2
   required type: char
@@ -13,6 +9,10 @@ proc rsplit(s: string; sep: char; maxsplit: int = -1): seq[string]
 proc rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[string]
   first type mismatch at position: 3
   unknown named parameter: maxsplits
+proc rsplit(s: string; sep: string; maxsplit: int = -1): seq[string]
+  first type mismatch at position: 2
+  required type: string
+  but expression '{':'}' is of type: set[char]
 
 expression: rsplit("abc:def", {':'}, maxsplits = 1)
 '''
diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim
index 5d5164f4f..c96a0a597 100644
--- a/tests/exception/t9657.nim
+++ b/tests/exception/t9657.nim
@@ -1,6 +1,8 @@
 discard """
   action: run
   exitcode: 1
+  target: "c"
 """
+# todo: remove `target: "c"` workaround once #10343 is properly fixed
 close stdmsg
 writeLine stdmsg, "exception!"
diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim
index b84ba7681..db46bad27 100644
--- a/tests/exception/tdefer1.nim
+++ b/tests/exception/tdefer1.nim
@@ -1,6 +1,5 @@
 discard """
   output: '''hi
-hi
 1
 hi
 2
@@ -10,13 +9,6 @@ A'''
 
 # bug #1742
 
-template test(): untyped =
-    let a = 0
-    defer: echo "hi"
-    a
-
-let i = test()
-
 import strutils
 let x = try: parseInt("133a")
         except: -1
@@ -31,7 +23,7 @@ template atFuncEnd =
 
 template testB(): untyped =
     let a = 0
-    defer: echo "hi" # Delete this line to make it work
+    defer: echo "hi"
     a
 
 proc main =
diff --git a/tests/float/tfloatnan.nim b/tests/float/tfloatnan.nim
index 29937a862..8f384c3d9 100644
--- a/tests/float/tfloatnan.nim
+++ b/tests/float/tfloatnan.nim
@@ -14,3 +14,31 @@ echo "Nim: ", f32, " (float)"
 
 let f64: float64 = NaN
 echo "Nim: ", f64, " (double)"
+
+block: # issue #10305
+  # with `-O3 -ffast-math`, generated C/C++ code is not nan compliant
+  # user can pass `--passC:-ffast-math` if he doesn't care.
+  proc fun() =
+    # this was previously failing at compile time with a nim compiler
+    # that was compiled with `nim cpp -d:release`
+    let a1 = 0.0
+    let a = 0.0/a1
+    let b1 = a == 0.0
+    let b2 = a == a
+    doAssert not b1
+    doAssert not b2
+
+  proc fun2(i: int) =
+    # this was previously failing simply with `nim cpp -d:release`; the
+    # difference with above example is that optimization (const folding) can't
+    # take place in this example to hide the non-compliant nan bug.
+    let a = 0.0/(i.float)
+    let b1 = a == 0.0
+    let b2 = a == a
+    doAssert not b1
+    doAssert not b2
+
+  static: fun()
+  fun()
+  fun2(0)
+
diff --git a/tests/gc/gcbench.nim b/tests/gc/gcbench.nim
index 782daf793..fc5d6864f 100644
--- a/tests/gc/gcbench.nim
+++ b/tests/gc/gcbench.nim
@@ -65,55 +65,58 @@ proc newNode(L, r: PNode): PNode =
 const
   kStretchTreeDepth = 18 # about 16Mb
   kLongLivedTreeDepth = 16  # about 4Mb
-  kArraySize  = 500000  # about 4Mb
+  kArraySize = 500000  # about 4Mb
   kMinTreeDepth = 4
   kMaxTreeDepth = 16
 
+when not declared(withScratchRegion):
+  template withScratchRegion(body: untyped) = body
+
 # Nodes used by a tree of a given size
-proc TreeSize(i: int): int = return ((1 shl (i + 1)) - 1)
+proc treeSize(i: int): int = return ((1 shl (i + 1)) - 1)
 
 # Number of iterations to use for a given tree depth
-proc NumIters(i: int): int =
-  return 2 * TreeSize(kStretchTreeDepth) div TreeSize(i)
+proc numIters(i: int): int =
+  return 2 * treeSize(kStretchTreeDepth) div treeSize(i)
 
 # Build tree top down, assigning to older objects.
-proc Populate(iDepth: int, thisNode: PNode) =
+proc populate(iDepth: int, thisNode: PNode) =
   if iDepth <= 0:
     return
   else:
     new(thisNode.left)
     new(thisNode.right)
-    Populate(iDepth-1, thisNode.left)
-    Populate(iDepth-1, thisNode.right)
+    populate(iDepth-1, thisNode.left)
+    populate(iDepth-1, thisNode.right)
 
 # Build tree bottom-up
-proc MakeTree(iDepth: int): PNode =
+proc makeTree(iDepth: int): PNode =
   if iDepth <= 0:
     new(result)
   else:
-    return newNode(MakeTree(iDepth-1), MakeTree(iDepth-1))
+    return newNode(makeTree(iDepth-1), makeTree(iDepth-1))
 
-proc PrintDiagnostics() =
+proc printDiagnostics() =
   echo("Total memory available: " & $getTotalMem() & " bytes")
   echo("Free memory: " & $getFreeMem() & " bytes")
 
-proc TimeConstruction(depth: int) =
+proc timeConstruction(depth: int) =
   var
     root, tempTree: PNode
     iNumIters: int
 
-  iNumIters = NumIters(depth)
+  iNumIters = numIters(depth)
 
   echo("Creating " & $iNumIters & " trees of depth " & $depth)
   var t = epochTime()
   for i in 0..iNumIters-1:
     new(tempTree)
-    Populate(depth, tempTree)
+    populate(depth, tempTree)
     tempTree = nil
   echo("\tTop down construction took " & $(epochTime() - t) & "msecs")
   t = epochTime()
   for i in 0..iNumIters-1:
-    tempTree = MakeTree(depth)
+    tempTree = makeTree(depth)
     tempTree = nil
   echo("\tBottom up construction took " & $(epochTime() - t) & "msecs")
 
@@ -127,39 +130,42 @@ proc main() =
 
   echo("Garbage Collector Test")
   echo(" Stretching memory with a binary tree of depth " & $kStretchTreeDepth)
-  PrintDiagnostics()
+  printDiagnostics()
   var t = epochTime()
 
   # Stretch the memory space quickly
-  tempTree = MakeTree(kStretchTreeDepth)
-  tempTree = nil
+  withScratchRegion:
+    tempTree = makeTree(kStretchTreeDepth)
+    tempTree = nil
 
   # Create a long lived object
   echo(" Creating a long-lived binary tree of depth " &
         $kLongLivedTreeDepth)
   new(longLivedTree)
-  Populate(kLongLivedTreeDepth, longLivedTree)
+  populate(kLongLivedTreeDepth, longLivedTree)
 
   # Create long-lived array, filling half of it
   echo(" Creating a long-lived array of " & $kArraySize & " doubles")
-  newSeq(myarray, kArraySize)
-  for i in 0..kArraySize div 2 - 1:
-    myarray[i] = 1.0 / toFloat(i)
+  withScratchRegion:
+    newSeq(myarray, kArraySize)
+    for i in 0..kArraySize div 2 - 1:
+      myarray[i] = 1.0 / toFloat(i)
 
-  PrintDiagnostics()
+    printDiagnostics()
 
-  var d = kMinTreeDepth
-  while d <= kMaxTreeDepth:
-    TimeConstruction(d)
-    inc(d, 2)
+    var d = kMinTreeDepth
+    while d <= kMaxTreeDepth:
+      withScratchRegion:
+        timeConstruction(d)
+      inc(d, 2)
 
-  if longLivedTree == nil or myarray[1000] != 1.0/1000.0:
-    echo("Failed")
-    # fake reference to LongLivedTree
-    # and array to keep them from being optimized away
+    if longLivedTree == nil or myarray[1000] != 1.0/1000.0:
+      echo("Failed")
+      # fake reference to LongLivedTree
+      # and array to keep them from being optimized away
 
   var elapsed = epochTime() - t
-  PrintDiagnostics()
+  printDiagnostics()
   echo("Completed in " & $elapsed & "ms. Success!")
 
 when defined(GC_setMaxPause):
diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim
index 24ac1036a..0b2e6e14d 100644
--- a/tests/gc/gcleak.nim
+++ b/tests/gc/gcleak.nim
@@ -12,7 +12,7 @@ type
 proc makeObj(): TTestObj =
   result.x = "Hello"
 
-for i in 1 .. 1_000_000:
+for i in 1 .. 100_000:
   when defined(gcMarkAndSweep) or defined(boehmgc):
     GC_fullcollect()
   var obj = makeObj()
diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim
index 09c07785e..2d8df7c1a 100644
--- a/tests/gc/thavlak.nim
+++ b/tests/gc/thavlak.nim
@@ -12,9 +12,10 @@ Found 1 loops (including artificial root node) (5)'''
 
 # bug #3184
 
-import tables
-import sequtils
-import sets
+import tables, sequtils, sets, strutils
+
+when not declared(withScratchRegion):
+  template withScratchRegion(body: untyped) = body
 
 type
   BasicBlock = object
@@ -418,8 +419,9 @@ proc run(self: var LoopTesterApp) =
   echo "15000 dummy loops"
 
   for i in 1..15000:
-    var h = newHavlakLoopFinder(self.cfg, newLsg())
-    var res = h.findLoops
+    withScratchRegion:
+      var h = newHavlakLoopFinder(self.cfg, newLsg())
+      var res = h.findLoops
 
   echo "Constructing CFG..."
   var n = 2
@@ -446,12 +448,17 @@ proc run(self: var LoopTesterApp) =
 
   var sum = 0
   for i in 1..5:
-    write stdout, "."
-    flushFile(stdout)
-    var hlf = newHavlakLoopFinder(self.cfg, newLsg())
-    sum += hlf.findLoops
-    #echo getOccupiedMem()
+    withScratchRegion:
+      write stdout, "."
+      flushFile(stdout)
+      var hlf = newHavlakLoopFinder(self.cfg, newLsg())
+      sum += hlf.findLoops
+      #echo getOccupiedMem()
   echo "\nFound ", loops, " loops (including artificial root node) (", sum, ")"
 
+  when false:
+    echo("Total memory available: " & formatSize(getTotalMem()) & " bytes")
+    echo("Free memory: " & formatSize(getFreeMem()) & " bytes")
+
 var l = newLoopTesterApp()
 l.run
diff --git a/tests/gc/tlists.nim b/tests/gc/tlists.nim
index 26b32396c..959cc5f7c 100644
--- a/tests/gc/tlists.nim
+++ b/tests/gc/tlists.nim
@@ -10,15 +10,13 @@ import lists
 import strutils
 
 proc mkleak() =
-    # allocate 10 MB via linked lists
+    # allocate 1 MB via linked lists
     let numberOfLists = 100
     for i in countUp(1, numberOfLists):
         var leakList = initDoublyLinkedList[string]()
-        let numberOfLeaks = 50000
+        let numberOfLeaks = 5000
         for j in countUp(1, numberOfLeaks):
-            let leakSize = 200
-            let leaked = newString(leakSize)
-            leakList.append(leaked)
+            leakList.append(newString(200))
 
 proc mkManyLeaks() =
     for i in 0..0:
@@ -29,7 +27,7 @@ proc mkManyLeaks() =
         # lists and bring the memory usage down to a few MB's.
         GC_fullCollect()
         when false: echo getOccupiedMem()
-        if getOccupiedMem() > 8 * 200 * 50_000 * 2:
+        if getOccupiedMem() > 8 * 200 * 5000 * 2:
           echo GC_getStatistics()
           quit "leaking"
     echo "Success"
diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim
index 2ef049ce2..31fa25293 100644
--- a/tests/generics/treentranttypes.nim
+++ b/tests/generics/treentranttypes.nim
@@ -1,6 +1,6 @@
 discard """
 output: '''
-(Field0: 10, Field1: (Field0: "test", Field1: 1.2))
+(10, ("test", 1.2))
 3x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0], [2.0, 0.0, 5.0]]
 
 2x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]]
diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim
index 701f652df..4469fdcf5 100644
--- a/tests/iter/titervaropenarray.nim
+++ b/tests/iter/titervaropenarray.nim
@@ -1,5 +1,6 @@
 discard """
   output: "123"
+  targets: "C"
 """
 # Try to break the transformation pass:
 iterator iterAndZero(a: var openArray[int]): int =
diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim
index 9aca6d45b..78c329a24 100644
--- a/tests/js/t9410.nim
+++ b/tests/js/t9410.nim
@@ -1,13 +1,3 @@
-template doAssert(exp: untyped) =
-  when defined(echot9410):
-    let r = exp
-    echo $(instantiationInfo().line) & ":\n  " & astToStr(exp) & "\n  was " & repr(r)
-    when not defined(noassertt9410):
-      system.doAssert r
-  else:
-    when not defined(noassertt9410):
-      system.doAssert exp
-
 template tests =
   block:
     var i = 0
@@ -428,6 +418,33 @@ template tests =
 
     let xptr2 = cast[type(xptr)](p2)
     doAssert xptr == xptr2
+  
+  block: # var types
+    block t10202:
+      type Point = object
+        x: float
+        y: float
+
+      var points: seq[Point]
+
+      points.add(Point(x:1, y:2))
+
+      for i, p in points.mpairs:
+        p.x += 1
+
+      doAssert points[0].x == 2
+    
+    block:
+      var ints = @[1, 2, 3]
+      for i, val in mpairs ints:
+        val *= 10
+      doAssert ints == @[10, 20, 30]
+    
+    block:
+      var seqOfSeqs = @[@[1, 2], @[3, 4]]
+      for i, val in mpairs seqOfSeqs:
+        val[0] *= 10
+      doAssert seqOfSeqs == @[@[10, 2], @[30, 4]]
 
   when false:
     block: # openarray
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
index 659c60092..70037f4bf 100644
--- a/tests/js/tclosures.nim
+++ b/tests/js/tclosures.nim
@@ -49,3 +49,47 @@ for i in 1 .. 10:
 let results = runCallbacks()
 
 doAssert(expected == $results)
+
+block issue7048:
+  block:
+    proc foo(x: seq[int]): auto =
+      proc bar: int = x[1]
+      bar
+
+    var stuff = @[1, 2]
+    let f = foo(stuff)
+    stuff[1] = 321
+    doAssert f() == 2
+
+  block:
+    proc foo(x: tuple[things: string]; y: array[3, int]): auto =
+      proc very: auto = 
+        proc deeply: auto =
+          proc nested: (char, int) = (x.things[0], y[1])
+          nested
+        deeply
+      very()
+
+    var
+      stuff = (things: "NIM")
+      stuff2 = [32, 64, 96]
+    let f = foo(stuff, stuff2)
+    stuff.things = "VIM"
+    stuff2[1] *= 10
+    doAssert f()() == ('N', 64)
+    doAssert (stuff.things[0], stuff2[1]) == ('V', 640)
+
+  block:
+    proc foo(x: ptr string): auto =
+      proc bar(): int = len(x[])
+      bar
+    
+    var 
+      s1 = "xyz"
+      s2 = "stuff"
+      p = addr s1
+    
+    let f = foo(p)
+    p = addr s2
+    doAssert len(p[]) == 5
+    doAssert f() == 3
\ No newline at end of file
diff --git a/tests/js/test1.nim b/tests/js/test1.nim
index 7f1d346f0..73e7a37ed 100644
--- a/tests/js/test1.nim
+++ b/tests/js/test1.nim
@@ -20,3 +20,35 @@ proc onButtonClick(inputElement: string) {.exportc.} =
 
 onButtonClick(inputElement)
 
+block:
+  var s: string
+  s.add("hi")
+  doAssert(s == "hi")
+
+block:
+  var s: string
+  s.insert("hi", 0)
+  doAssert(s == "hi")
+
+block:
+  var s: string
+  s.setLen(2)
+  s[0] = 'h'
+  s[1] = 'i'
+  doAssert(s == "hi")
+
+block:
+  var s: seq[int]
+  s.setLen(2)
+  doAssert(s == @[0, 0])
+
+block:
+  var s: seq[int]
+  s.insert(2, 0)
+  doAssert(s == @[2])
+
+block:
+  var s: seq[int]
+  s.add(2)
+  doAssert(s == @[2])
+
diff --git a/tests/js/test2.nim b/tests/js/test2.nim
index 0bfb99139..9ecdbb35c 100644
--- a/tests/js/test2.nim
+++ b/tests/js/test2.nim
@@ -9,6 +9,9 @@ js 3.14
 
 # This file tests the JavaScript generator
 
+doAssert getCurrentException() == nil
+doAssert getCurrentExceptionMsg() == ""
+
 #  #335
 proc foo() =
   var bar = "foo"
diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim
index 78f0b4766..b61d06471 100644
--- a/tests/js/testobjs.nim
+++ b/tests/js/testobjs.nim
@@ -54,3 +54,20 @@ let test2 = test1
 
 echo toJSON(test1)
 echo toJSON(test2)
+
+block issue10005:
+  type
+    Player = ref object of RootObj
+      id*: string
+      nickname*: string
+      color*: string
+
+  proc newPlayer(nickname: string, color: string): Player =
+    let pl = Player(color: "#123", nickname: nickname)
+    return Player(
+        id: "foo",
+        nickname: nickname,
+        color: color,
+    )
+
+  doAssert newPlayer("foo", "#1232").nickname == "foo"
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 2420c60f6..8bd40a3c4 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -22,6 +22,13 @@ true
 Event { name: 'click: test' }
 Event { name: 'reloaded: test' }
 Event { name: 'updates: test' }
+true
+true
+true
+true
+true
+true
+true
 '''
 """
 
@@ -317,3 +324,12 @@ block:
   jslib.subscribe("updates"):
     console.log jsarguments[0]
 
+block:
+
+  echo jsUndefined == jsNull
+  echo jsUndefined == nil
+  echo jsNull == nil
+  echo jsUndefined.isNil
+  echo jsNull.isNil
+  echo jsNull.isNull
+  echo jsUndefined.isUndefined
diff --git a/tests/macros/tquotedo.nim b/tests/macros/tquotedo.nim
index cd1f69116..0aae87bf0 100644
--- a/tests/macros/tquotedo.nim
+++ b/tests/macros/tquotedo.nim
@@ -3,6 +3,7 @@ output: '''
 123
 Hallo Welt
 Hallo Welt
+1
 '''
 """
 
@@ -23,3 +24,13 @@ macro foobar(arg: untyped): untyped =
 
 foobar:
   echo "Hallo Welt"
+
+# bug #3744
+import macros
+macro t(): untyped =
+  return quote do:
+    proc tp(): int =
+      result = 1
+t()
+
+echo tp()
diff --git a/tests/macros/tvarargsuntyped.nim b/tests/macros/tvarargsuntyped.nim
index 657ed47d6..5a06adcca 100644
--- a/tests/macros/tvarargsuntyped.nim
+++ b/tests/macros/tvarargsuntyped.nim
@@ -3,7 +3,9 @@ discard """
 (left: 2, r: 7, x: 8, height: 4, s: test, width: 3, y: 9, top: 1, g: 7, b: 8)
 (left: 2, r: 7, x: 8, height: 4, s: text, width: 3, y: 9, top: 1, g: 7, b: 8)
 (left: 2, r: 7, x: 8, height: 4, s: text, width: 3, y: 9, top: 4, g: 7, b: 8)
-(left: 2, r: 7, x: 8, height: 4, s: test, width: 3, y: 9, top: 1, g: 7, b: 8)'''
+(left: 2, r: 7, x: 8, height: 4, s: test, width: 3, y: 9, top: 1, g: 7, b: 8)
+10
+hello 18.0'''
 """
 
 import macros
@@ -78,3 +80,29 @@ let width: cint = 3
 let height: cint = 4
 
 bar(rect(top, left, width, height), "test", point(8, 9), color(7,7,8))
+
+
+# bug #10075
+
+import macros
+
+proc convert_hidden_stdconv(args: NimNode): NimNode =
+  var n = args
+  while n.len == 1 and n[0].kind == nnkHiddenStdConv:
+    n = n[0][1]
+  return n
+
+macro t2(s: int, v: varargs[untyped]): untyped =
+  let v = convert_hidden_stdconv(v)
+  echo v.treeRepr
+  let (v1, v2) = (v[0], v[1])
+  quote do:
+    echo `v1`, " ", `v2`
+
+template t1(s: int, v: varargs[typed]) =
+  #static:
+  #   dumpTree v
+  echo s
+  t2(s, v)
+
+t1(10, "hello", 18.0)
diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim
index 3bb259386..336e57755 100644
--- a/tests/manyloc/keineschweine/enet_server/enet_server.nim
+++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim
@@ -103,7 +103,7 @@ handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) =
 
 
 when true:
-  import parseopt, matchers, os, json
+  import parseopt, os, json
 
 
   if enetInit() != 0:
diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim
index cfb0b0377..390b738aa 100644
--- a/tests/manyloc/keineschweine/server/old_dirserver.nim
+++ b/tests/manyloc/keineschweine/server/old_dirserver.nim
@@ -157,7 +157,7 @@ proc poll*(timeout: int = 250) =
       c.outputBuf.flush()
 
 when true:
-  import parseopt, matchers, strutils
+  import parseopt, strutils
   var cfgFile = "dirserver_settings.json"
   for kind, key, val in getOpt():
     case kind
diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim
index d046df9dd..473880e2b 100644
--- a/tests/manyloc/keineschweine/server/old_sg_server.nim
+++ b/tests/manyloc/keineschweine/server/old_sg_server.nim
@@ -142,7 +142,7 @@ proc poll*(timeout: int = 250) =
       c.outputBuf.flush()
 
 when true:
-  import parseopt, matchers, strutils
+  import parseopt, strutils
   var zoneCfgFile = "./server_settings.json"
   for kind, key, val in getOpt():
     case kind
diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim
index 3e8609169..35ed3cbb0 100644
--- a/tests/manyloc/nake/nakefile.nim
+++ b/tests/manyloc/nake/nakefile.nim
@@ -76,13 +76,14 @@ task "testskel", "create skeleton test dir for testing":
 
 task "clean", "cleanup generated files":
   var dirs = @["nimcache", "server"/"nimcache"]
-  dirs.map(proc(x: var string) =
+  dirs.apply(proc(x: var string) =
     if existsDir(x): removeDir(x))
 
 task "download", "download game assets":
   var
     skipAssets = false
     path = expandFilename("data")
+    client = newHttpClient()
   path.add DirSep
   path.add(extractFilename(GameAssets))
   if existsFile(path):
@@ -101,7 +102,7 @@ task "download", "download game assets":
     echo "Downloading from ", GameAssets
   if not skipAssets:
     echo "Downloading to ", path
-    downloadFile GameAssets, path
+    client.downloadFile(GameAssets, path)
     echo "Download finished"
 
     let targetDir = parentDir(parentDir(path))
@@ -126,7 +127,7 @@ task "download", "download game assets":
   else:
     return
   path = extractFilename(BinLibs)
-  downloadFile BinLibs, path
+  client.downloadFile(BinLibs, path)
   echo "Downloaded dem libs ", path
   when true: echo "Unpack it yourself, sorry."
   else:  ## this crashes, dunno why
diff --git a/tests/manyloc/standalone/barebone.nim b/tests/manyloc/standalone/barebone.nim
index 9d75f8f2e..0b38616b2 100644
--- a/tests/manyloc/standalone/barebone.nim
+++ b/tests/manyloc/standalone/barebone.nim
@@ -1,4 +1,8 @@
-
+discard """
+ccodecheck: "\\i !@('systemInit')"
+ccodecheck: "\\i !@('systemDatInit')"
+output: "hello"
+"""
 # bug  #2041: Macros need to be available for os:standalone!
 import macros
 
diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim
index c5040f9ba..c184689a1 100644
--- a/tests/metatype/tmetatype_issues.nim
+++ b/tests/metatype/tmetatype_issues.nim
@@ -1,7 +1,7 @@
 discard """
 output:'''
 void
-(Field0: "string", Field1: "string")
+("string", "string")
 1 mod 7
 @[2, 2, 2, 2, 2]
 impl 2 called
@@ -9,6 +9,7 @@ asd
 Foo
 Bar
 '''
+joinable: false
 """
 
 import typetraits, macros
diff --git a/tests/metatype/ttypetraits2.nim b/tests/metatype/ttypetraits2.nim
new file mode 100644
index 000000000..a436da7ec
--- /dev/null
+++ b/tests/metatype/ttypetraits2.nim
@@ -0,0 +1,18 @@
+# todo: merge with $nimc_D/tests/metatype/ttypetraits.nim (currently disabled)
+
+from typetraits import `$` # checks fix for https://github.com/c-blake/cligen/issues/84
+
+import typetraits
+
+block: # isNamedTuple
+  type Foo1 = (a:1,).type
+  type Foo2 = (Field0:1,).type
+  type Foo3 = ().type
+  type Foo4 = object
+
+  doAssert (a:1,).type.isNamedTuple
+  doAssert Foo1.isNamedTuple
+  doAssert Foo2.isNamedTuple
+  doAssert not Foo3.isNamedTuple
+  doAssert not Foo4.isNamedTuple
+  doAssert not (1,).type.isNamedTuple
diff --git a/tests/method/tmapper.nim b/tests/method/tmapper.nim
index a5d03f700..9162d0eec 100644
--- a/tests/method/tmapper.nim
+++ b/tests/method/tmapper.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22, 7)"
+  errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22, 8)"
   line: 25
 """
 
diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim
index cbed5d476..7d5071c3e 100644
--- a/tests/misc/tparseopt.nim
+++ b/tests/misc/tparseopt.nim
@@ -29,55 +29,120 @@ kind: cmdLongOption	key:val  --  left:
 kind: cmdLongOption	key:val  --  debug:3
 kind: cmdShortOption	key:val  --  l:4
 kind: cmdShortOption	key:val  --  r:2'''
+joinable: false
 """
-from parseopt import nil
-from parseopt2 import nil
 
+when defined(testament_tparseopt):
+  import os
+  proc main() =
+    let args = commandLineParams()
+    echo args
+    for i, ai in args:
+      echo "arg ", i, " ai.len:", ai.len, " :{", ai, "}"
+  main()
+else:
+  from parseopt import nil
+  from parseopt2 import nil
 
-block:
-  echo "parseopt"
-  for kind, key, val in parseopt.getopt():
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+  block:
+    echo "parseopt"
+    for kind, key, val in parseopt.getopt():
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
 
-  # pass custom cmdline arguments
-  echo "first round"
-  var argv = "--left --debug:3 -l=4 -r:2"
-  var p = parseopt.initOptParser(argv)
-  for kind, key, val in parseopt.getopt(p):
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
-    break
-  # reset getopt iterator and check arguments are returned correctly.
-  echo "second round"
-  for kind, key, val in parseopt.getopt(p):
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+    # pass custom cmdline arguments
+    echo "first round"
+    var argv = "--left --debug:3 -l=4 -r:2"
+    var p = parseopt.initOptParser(argv)
+    for kind, key, val in parseopt.getopt(p):
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+      break
+    # reset getopt iterator and check arguments are returned correctly.
+    echo "second round"
+    for kind, key, val in parseopt.getopt(p):
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
 
-  # bug #9619
-  var x = parseopt.initOptParser(@["--foo:", "--path"], allowWhitespaceAfterColon = false)
-  for kind, key, val in parseopt.getopt(x):
-    echo kind, " ", key
+    # bug #9619
+    var x = parseopt.initOptParser(@["--foo:", "--path"],
+        allowWhitespaceAfterColon = false)
+    for kind, key, val in parseopt.getopt(x):
+      echo kind, " ", key
 
-block:
-  echo "parseoptNoVal"
-  # test NoVal mode with custom cmdline arguments
-  var argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4"
-  var p = parseopt.initOptParser(argv,
-                                  shortNoVal = {'l'}, longNoVal = @["left"])
-  for kind, key, val in parseopt.getopt(p):
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+  block:
+    echo "parseoptNoVal"
+    # test NoVal mode with custom cmdline arguments
+    var argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4"
+    var p = parseopt.initOptParser(argv,
+                                    shortNoVal = {'l'}, longNoVal = @["left"])
+    for kind, key, val in parseopt.getopt(p):
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
 
-block:
-  echo "parseopt2"
-  for kind, key, val in parseopt2.getopt():
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+  block:
+    echo "parseopt2"
+    for kind, key, val in parseopt2.getopt():
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
 
-  # pass custom cmdline arguments
-  echo "first round"
-  var argv: seq[string] = @["--left", "--debug:3", "-l=4", "-r:2"]
-  var p = parseopt2.initOptParser(argv)
-  for kind, key, val in parseopt2.getopt(p):
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
-    break
-  # reset getopt iterator and check arguments are returned correctly.
-  echo "second round"
-  for kind, key, val in parseopt2.getopt(p):
-    echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+    # pass custom cmdline arguments
+    echo "first round"
+    var argv: seq[string] = @["--left", "--debug:3", "-l=4", "-r:2"]
+    var p = parseopt2.initOptParser(argv)
+    for kind, key, val in parseopt2.getopt(p):
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+      break
+    # reset getopt iterator and check arguments are returned correctly.
+    echo "second round"
+    for kind, key, val in parseopt2.getopt(p):
+      echo "kind: ", kind, "\tkey:val  --  ", key, ":", val
+
+  import osproc, os, strutils
+  from stdtest/specialpaths import buildDir
+  import "../.." / compiler/unittest_light
+
+  block: # fix #9951 (and make it work for parseopt and parseopt2)
+    template runTest(parseoptCustom) =
+      var p = parseoptCustom.initOptParser(@["echo \"quoted\""])
+      let expected = when defined(windows):
+        """"echo \"quoted\"""""
+      else:
+        """'echo "quoted"'"""
+      assertEquals parseoptCustom.cmdLineRest(p), expected
+
+      doAssert "a5'b" == "a5\'b"
+
+      let args = @["a1b", "a2 b", "", "a4\"b", "a5'b", r"a6\b", "a7\'b"]
+      var p2 = parseoptCustom.initOptParser(args)
+      let expected2 = when defined(windows):
+        """a1b "a2 b" "" a4\"b a5'b a6\b a7'b"""
+      else:
+        """a1b 'a2 b' '' 'a4"b' 'a5'"'"'b' 'a6\b' 'a7'"'"'b'"""
+      doAssert "a5'b" == "a5\'b"
+      assertEquals parseoptCustom.cmdLineRest(p2), expected2
+    runTest(parseopt)
+    runTest(parseopt2)
+
+  block: # fix #9842
+    let exe = buildDir / "D20190112T145450".addFileExt(ExeExt)
+    defer:
+      when not defined(windows):
+        # workaround #10359 ; innocuous to skip since we're saving under `buildDir`
+        removeFile exe
+    let args = @["a1b", "a2 b", "", "a4\"b", "a5'b", r"a6\b", "a7\'b"]
+    let cmd = "$# c -r --verbosity:0 -o:$# -d:testament_tparseopt $# $#" %
+      [getCurrentCompilerExe(), exe, currentSourcePath(),
+          args.quoteShellCommand]
+    var ret = execCmdEx(cmd, options = {})
+    if ret.exitCode != 0:
+      # before bug fix, running cmd would show:
+      # sh: -c: line 0: unexpected EOF while looking for matching `"'\n
+      echo "exitCode: ", ret.exitCode, " cmd:", cmd
+      doAssert false
+    stripLineEnd(ret.output)
+    assertEquals ret.output,
+      """
+@["a1b", "a2 b", "", "a4\"b", "a5\'b", "a6\\b", "a7\'b"]
+arg 0 ai.len:3 :{a1b}
+arg 1 ai.len:4 :{a2 b}
+arg 2 ai.len:0 :{}
+arg 3 ai.len:4 :{a4"b}
+arg 4 ai.len:4 :{a5'b}
+arg 5 ai.len:4 :{a6\b}
+arg 6 ai.len:4 :{a7'b}"""
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index f60c7fa00..25c566171 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -36,27 +36,24 @@ macro testSizeAlignOf(args: varargs[untyped]): untyped =
       if nim_size != c_size or nim_align != c_align:
         var msg = strAlign(`arg`.type.name & ": ")
         if nim_size != c_size:
-          msg.add  " size(got, expected):  " & $nim_size  & " != " & $c_size
+          msg.add  " size(got, expected):  " & $nim_size & " != " & $c_size
         if nim_align != c_align:
           msg.add  " align(get, expected): " & $nim_align & " != " & $c_align
         echo msg
         failed = true
 
 
-macro testOffsetOf(a,b1,b2: untyped): untyped =
+macro testOffsetOf(a, b: untyped): untyped =
   let typeName = newLit(a.repr)
-  let member   = newLit(b2.repr)
+  let member   = newLit(b.repr)
   result = quote do:
     let
-      c_offset   = c_offsetof(`a`,`b1`)
-      nim_offset = offsetof(`a`,`b2`)
+      c_offset   = c_offsetof(`a`,`b`)
+      nim_offset = offsetof(`a`,`b`)
     if c_offset != nim_offset:
       echo `typeName`, ".", `member`, " offsetError, C: ", c_offset, " nim: ", nim_offset
       failed = true
 
-template testOffsetOf(a,b: untyped): untyped =
-  testOffsetOf(a,b,b)
-
 proc strAlign(arg: string): string =
   const minLen = 22
   result = arg
@@ -337,16 +334,16 @@ testinstance:
     testOffsetOf(AlignAtEnd, b)
     testOffsetOf(AlignAtEnd, c)
 
-    testOffsetOf(SimpleBranch, "_Ukind", a)
-    testOffsetOf(SimpleBranch, "_Ukind", b)
-    testOffsetOf(SimpleBranch, "_Ukind", c)
+    testOffsetOf(SimpleBranch, a)
+    testOffsetOf(SimpleBranch, b)
+    testOffsetOf(SimpleBranch, c)
 
     testOffsetOf(PaddingBeforeBranchA, cause)
-    testOffsetOf(PaddingBeforeBranchA, "_Ukind", a)
+    testOffsetOf(PaddingBeforeBranchA, a)
     testOffsetOf(PaddingBeforeBranchB, cause)
-    testOffsetOf(PaddingBeforeBranchB, "_Ukind", a)
+    testOffsetOf(PaddingBeforeBranchB, a)
 
-    testOffsetOf(PaddingAfterBranch, "_Ukind", a)
+    testOffsetOf(PaddingAfterBranch, a)
     testOffsetOf(PaddingAfterBranch, cause)
 
     testOffsetOf(Foobar, c)
@@ -367,15 +364,15 @@ testinstance:
     testOffsetOf(EnumObjectB, d)
 
     testOffsetOf(RecursiveStuff, kind)
-    testOffsetOf(RecursiveStuff, "_Ukind.S1.a",              a)
-    testOffsetOf(RecursiveStuff, "_Ukind.S2.b",              b)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3.kind2",          kind2)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S1.ca1", ca1)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S1.ca2", ca2)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S2.cb",  cb)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S3.cc",  cc)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3.d1",             d1)
-    testOffsetOf(RecursiveStuff, "_Ukind.S3.d2",             d2)
+    testOffsetOf(RecursiveStuff, a)
+    testOffsetOf(RecursiveStuff, b)
+    testOffsetOf(RecursiveStuff, kind2)
+    testOffsetOf(RecursiveStuff, ca1)
+    testOffsetOf(RecursiveStuff, ca2)
+    testOffsetOf(RecursiveStuff, cb)
+    testOffsetOf(RecursiveStuff, cc)
+    testOffsetOf(RecursiveStuff, d1)
+    testOffsetOf(RecursiveStuff, d2)
 
   main()
 
@@ -394,6 +391,29 @@ type
 
 assert sizeof(Bar) == 12
 
+# bug #10082
+type
+  A = int8        # change to int16 and get sizeof(C)==6
+  B = int16
+  C = object {.packed.}
+    d {.bitsize:  1.}: A
+    e {.bitsize:  7.}: A
+    f {.bitsize: 16.}: B
+
+assert sizeof(C) == 3
+
+
+type
+  MixedBitsize = object {.packed.}
+    a: uint32
+    b {.bitsize:  8.}: uint8
+    c {.bitsize:  1.}: uint8
+    d {.bitsize:  7.}: uint8
+    e {.bitsize: 16.}: uint16
+    f: uint32
+
+doAssert sizeof(MixedBitsize) == 12
+
 if failed:
   quit("FAIL")
 else:
diff --git a/tests/modules/mimport_in_config.nim b/tests/modules/mimport_in_config.nim
new file mode 100644
index 000000000..555b6074d
--- /dev/null
+++ b/tests/modules/mimport_in_config.nim
@@ -0,0 +1,2 @@
+type
+  DefinedInB* = int
diff --git a/tests/modules/timport_in_config.nim b/tests/modules/timport_in_config.nim
new file mode 100644
index 000000000..847b063bd
--- /dev/null
+++ b/tests/modules/timport_in_config.nim
@@ -0,0 +1,9 @@
+discard """
+output: '''hallo'''
+joinable: false
+"""
+
+# bug #9978, #9994
+var x: DefinedInB
+
+echo "hi".replace("i", "allo")
diff --git a/tests/modules/timport_in_config.nim.cfg b/tests/modules/timport_in_config.nim.cfg
new file mode 100644
index 000000000..2633e1012
--- /dev/null
+++ b/tests/modules/timport_in_config.nim.cfg
@@ -0,0 +1,2 @@
+--import: "strutils"
+--import: "mimport_in_config"
diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim
index 4bf244807..fd582b571 100644
--- a/tests/modules/tmismatchedvisibility.nim
+++ b/tests/modules/tmismatchedvisibility.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6, 5)]' has non-public forward declaration in "
+  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6, 6)]' has non-public forward declaration in "
   line: 8
 """
 
diff --git a/tests/nim.cfg b/tests/nim.cfg
new file mode 100644
index 000000000..577baaacd
--- /dev/null
+++ b/tests/nim.cfg
@@ -0,0 +1 @@
+--path:"../testament/lib" # so we can `import stdtest/foo` in this dir
diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim
index a49e08911..1c4d258fb 100644
--- a/tests/niminaction/Chapter8/sdl/sdl_test.nim
+++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim
@@ -17,12 +17,24 @@ discard pollEvent(nil)
 renderer.setDrawColor 29, 64, 153, 255
 renderer.clear
 renderer.setDrawColor 255, 255, 255, 255
-var points = [
-  (260'i32, 320'i32),
-  (260'i32, 110'i32),
-  (360'i32, 320'i32),
-  (360'i32, 110'i32)
-]
+
+when defined(c):
+  # just to ensure code from NimInAction still works, but
+  # the `else` branch would work as well in C mode
+  var points = [
+    (260'i32, 320'i32),
+    (260'i32, 110'i32),
+    (360'i32, 320'i32),
+    (360'i32, 110'i32)
+  ]
+else:
+  var points = [
+    (260.cint, 320.cint),
+    (260.cint, 110.cint),
+    (360.cint, 320.cint),
+    (360.cint, 110.cint)
+  ]
+
 renderer.drawLines(addr points[0], points.len.cint)
 
 renderer.present
diff --git a/tests/objects/t3734.nim b/tests/objects/t3734.nim
new file mode 100644
index 000000000..cebef6081
--- /dev/null
+++ b/tests/objects/t3734.nim
@@ -0,0 +1,17 @@
+discard """
+output: "i0"
+"""
+
+type
+  Application = object
+      config: void
+      i: int
+      f: void
+
+proc printFields(rec: Application) =
+  for k, v in fieldPairs(rec):
+    echo k, v
+
+var app: Application
+
+printFields(app)
diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim
index 817c1fcda..6c587e04d 100644
--- a/tests/objects/tobjcov.nim
+++ b/tests/objects/tobjcov.nim
@@ -1,8 +1,12 @@
 discard """
 action: compile
+target: "c"
 """
 
 # Covariance is not type safe:
+# Note: `nim cpp` makes it a compile error (after codegen), even with:
+# `var f = cast[proc (x: var TA) {.nimcall.}](cast[pointer](bp))`, which
+# currently removes all the `cast` in cgen'd code, hence the compile error.
 
 type
   TA = object of RootObj
diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim
index 61ef7442e..fbf531c3d 100644
--- a/tests/objects/tobject.nim
+++ b/tests/objects/tobject.nim
@@ -17,3 +17,23 @@ suite "object basic methods":
     check($obj == "(foo: 1)")
   test "it should test equality based on fields":
     check(makeObj(1) == makeObj(1))
+
+# bug #10203
+
+type
+  TMyObj = TYourObj
+  TYourObj = object of RootObj
+    x, y: int
+
+proc init: TYourObj =
+  result.x = 0
+  result.y = -1
+
+proc f(x: var TYourObj) =
+  discard
+
+var m: TMyObj = init()
+f(m)
+
+var a: TYourObj = m
+var b: TMyObj = a
diff --git a/tests/objvariant/tyaoption.nim b/tests/objvariant/tyaoption.nim
index 7a29b8008..80bfa4bae 100644
--- a/tests/objvariant/tyaoption.nim
+++ b/tests/objvariant/tyaoption.nim
@@ -1,7 +1,8 @@
 discard """
   output: '''some(str), some(5), none
 some(5!)
-some(10)'''
+some(10)
+34'''
 """
 
 import strutils
@@ -45,3 +46,25 @@ let a3 = intOrString(some(5))
 #echo a1
 echo a2
 echo a3
+
+
+# bug #10033
+
+type
+  Token = enum
+    Int,
+    Float
+
+  Base = ref object of RootObj
+    case token: Token
+    of Int:
+      bInt: int
+    of Float:
+      bFloat: float
+
+  Child = ref object of Base
+
+let c = new Child
+c.token = Int
+c.bInt = 34
+echo c.bInt
diff --git a/tests/parallel/twrong_refcounts.nim b/tests/parallel/twrong_refcounts.nim
index ac428762d..ed3c1b894 100644
--- a/tests/parallel/twrong_refcounts.nim
+++ b/tests/parallel/twrong_refcounts.nim
@@ -1,7 +1,10 @@
 discard """
   output: "Success"
+  target: "c"
 """
 
+# Note: target: "cpp" fails because we can't yet have `extern "C"` mangling in
+# `exportc` procs.
 import math, random, threadPool
 
 # ---
diff --git a/tests/pragmas/t5149.nim b/tests/pragmas/t5149.nim
new file mode 100644
index 000000000..2d242a8d5
--- /dev/null
+++ b/tests/pragmas/t5149.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "{.exportc.} not allowed for type aliases"
+  line: 9
+"""
+
+type
+  X* = object
+    a: int
+  Y* {.exportc.} = X
+
+proc impl*(x: X) =
+  echo "it works"
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index 0bc4d2f18..e04d3de26 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -175,24 +175,56 @@ var foo: Something
 foo.cardinal = north
 doAssert foo.b.hasCustomPragma(thingy) == true
 
-
-proc myproc(s: string): int = 
+proc myproc(s: string): int =
   {.thingy.}:
     s.len
 
 doAssert myproc("123") == 3
 
 let xx = compiles:
-  proc myproc_bad(s: string): int = 
+  proc myproc_bad(s: string): int =
     {.not_exist.}:
       s.len
 doAssert: xx == false
 
-
-macro checkSym(s: typed{nkSym}): untyped = 
+macro checkSym(s: typed{nkSym}): untyped =
   let body = s.getImpl.body
   doAssert body[1].kind == nnkPragmaBlock
   doAssert body[1][0].kind == nnkPragma
   doAssert body[1][0][0] == bindSym"thingy"
 
-checkSym(myproc)
\ No newline at end of file
+checkSym(myproc)
+
+# var and let pragmas
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  let a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  let b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+  var x {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  var y {.myAttr,myAttr2(2),myAttr3:"test".}: int
+  var z {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+
+  template check(s: untyped) =
+    doAssert s.hasCustomPragma(myAttr)
+    doAssert s.hasCustomPragma(myAttr2)
+    doAssert s.getCustomPragmaVal(myAttr2) == 2
+    doAssert s.hasCustomPragma(myAttr3)
+    doAssert s.getCustomPragmaVal(myAttr3) == "test"
+
+  check(a)
+  check(b)
+  check(x)
+  check(y)
+  check(z)
+
+# pragma with multiple fields
+block:
+  template myAttr(first: string, second: int, third: float) {.pragma.}
+  let a {.myAttr("one", 2, 3.0).} = 0
+  let ps = a.getCustomPragmaVal(myAttr)
+  doAssert ps.first == ps[0] and ps.first == "one"
+  doAssert ps.second == ps[1] and ps.second == 2
+  doAssert ps.third == ps[2] and ps.third == 3.0
diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim
index 7616c1215..d0c533f9a 100644
--- a/tests/pragmas/tused.nim
+++ b/tests/pragmas/tused.nim
@@ -1,7 +1,7 @@
 discard """
   nimout: '''
 compile start
-tused.nim(17, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(17, 7)]' is declared but not used [XDeclaredButNotUsed]
+tused.nim(17, 8) Hint: 'echoSub' is declared but not used [XDeclaredButNotUsed]
 compile end'''
   output: "8\n8"
   joinable: false
diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim
new file mode 100644
index 000000000..be9e2147e
--- /dev/null
+++ b/tests/proc/tillegalreturntype.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim check $file"
+  errmsg: ""
+  nimout: '''tillegalreturntype.nim(8, 11) Error: return type 'typed' is only valid for macros and templates
+tillegalreturntype.nim(11, 11) Error: return type 'untyped' is only valid for macros and templates'''
+"""
+
+proc x(): typed =
+  discard
+
+proc y(): untyped =
+  discard
diff --git a/tests/proc/typed.nim b/tests/proc/typed.nim
new file mode 100644
index 000000000..2e8117634
--- /dev/null
+++ b/tests/proc/typed.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "'typed' is only allowed in templates and macros"
+  line: 6
+"""
+
+proc fun(x:typed)=discard
+fun(10)
diff --git a/tests/proc/untyped.nim b/tests/proc/untyped.nim
new file mode 100644
index 000000000..f8b3ead7b
--- /dev/null
+++ b/tests/proc/untyped.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "'untyped' is only allowed in templates and macros"
+  line: 6
+"""
+
+proc fun(x:untyped)=discard
+fun(10)
diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim
index 2a3b7332d..b7cde6124 100644
--- a/tests/statictypes/tstatictypes.nim
+++ b/tests/statictypes/tstatictypes.nim
@@ -116,3 +116,24 @@ block:
     Tensor[B: static[Backend]; T] = object
 
     BackProp[B: static[Backend],T] = proc (gradient: Tensor[B,T]): Tensor[B,T]
+
+# https://github.com/nim-lang/Nim/issues/10073
+block:
+  proc foo[N: static int](x: var int,
+                          y: int,
+                          z: static int,
+                          arr: array[N, int]): auto =
+    var t1 = (a: x, b: y, c: z, d: N)
+    var t2 = (x, y, z, N)
+    doAssert t1 == t2
+    result = t1
+
+  var y = 20
+  var x = foo(y, 10, 15, [1, 2, 3])
+  doAssert x == (20, 10, 15, 3)
+
+# #7609
+block:
+  type
+    Coord[N: static[int]] = tuple[col, row: range[0'i8 .. (N.int8-1)]]
+    Point[N: static[int]] = range[0'i16 .. N.int16 * N.int16 - 1]
diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim
new file mode 100644
index 000000000..2bb64b475
--- /dev/null
+++ b/tests/stdlib/t10231.nim
@@ -0,0 +1,15 @@
+discard """
+  target: cpp
+  action: run
+  exitcode: 0
+"""
+
+import os
+
+# consider moving this inside tosproc (taking care that it's for cpp mode)
+
+if paramCount() == 0:
+  # main process
+  doAssert execShellCmd(getAppFilename().quoteShell & " test") == 1
+else:
+  quit 1
diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim
new file mode 100644
index 000000000..39102e131
--- /dev/null
+++ b/tests/stdlib/tgetaddrinfo.nim
@@ -0,0 +1,36 @@
+discard """
+  exitcode: 0
+  output: ""
+"""
+
+# bug: https://github.com/nim-lang/Nim/issues/10198
+
+import nativesockets
+
+block DGRAM_UDP:
+  let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  doAssert aiList != nil
+  doAssert aiList.ai_addr != nil
+  doAssert aiList.ai_addrlen == 16
+  doAssert aiList.ai_next == nil
+  freeAddrInfo aiList
+
+when defined(posix):
+
+  block RAW_ICMP:
+    # the port will be ignored
+    let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_RAW, IPPROTO_ICMP)
+    doAssert aiList != nil
+    doAssert aiList.ai_addr != nil
+    doAssert aiList.ai_addrlen == 16
+    doAssert aiList.ai_next == nil
+    freeAddrInfo aiList
+
+  block RAW_ICMPV6:
+    # the port will be ignored
+    let aiList = getAddrInfo("::1", 999.Port, AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)
+    doAssert aiList != nil
+    doAssert aiList.ai_addr != nil
+    doAssert aiList.ai_addrlen == 28
+    doAssert aiList.ai_next == nil
+    freeAddrInfo aiList
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index 33332447b..2e95b4833 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -516,3 +516,7 @@ when true:
       var w = u.to(MyDistRef)
       doAssert v.name == "smith"
       doAssert MyRef(w).name == "smith"
+
+    block test_tuple:
+      doAssert $(%* (a1: 10, a2: "foo")) == """{"a1":10,"a2":"foo"}"""
+      doAssert $(%* (10, "foo")) == """[10,"foo"]"""
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 581308a7e..bdb5aa332 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -4,6 +4,10 @@ discard """
 
 [Suite] random float
 
+[Suite] cumsum
+
+[Suite] random sample
+
 [Suite] ^
 
 '''
@@ -11,34 +15,34 @@ discard """
 
 import math, random, os
 import unittest
-import sets
+import sets, tables
 
 suite "random int":
   test "there might be some randomness":
     var set = initSet[int](128)
-    randomize()
+
     for i in 1..1000:
       incl(set, random(high(int)))
     check len(set) == 1000
   test "single number bounds work":
-    randomize()
+
     var rand: int
     for i in 1..1000:
       rand = random(1000)
       check rand < 1000
       check rand > -1
   test "slice bounds work":
-    randomize()
+
     var rand: int
     for i in 1..1000:
       rand = random(100..1000)
       check rand < 1000
       check rand >= 100
-  test "randomize() again gives new numbers":
-    randomize()
+  test " again gives new numbers":
+
     var rand1 = random(1000000)
     os.sleep(200)
-    randomize()
+
     var rand2 = random(1000000)
     check rand1 != rand2
 
@@ -46,32 +50,93 @@ suite "random int":
 suite "random float":
   test "there might be some randomness":
     var set = initSet[float](128)
-    randomize()
+
     for i in 1..100:
       incl(set, random(1.0))
     check len(set) == 100
   test "single number bounds work":
-    randomize()
+
     var rand: float
     for i in 1..1000:
       rand = random(1000.0)
       check rand < 1000.0
       check rand > -1.0
   test "slice bounds work":
-    randomize()
+
     var rand: float
     for i in 1..1000:
       rand = random(100.0..1000.0)
       check rand < 1000.0
       check rand >= 100.0
-  test "randomize() again gives new numbers":
-    randomize()
+  test " again gives new numbers":
+
     var rand1:float = random(1000000.0)
     os.sleep(200)
-    randomize()
+
     var rand2:float = random(1000000.0)
     check rand1 != rand2
 
+suite "cumsum":
+  test "cumsum int seq return":
+    let counts = [ 1, 2, 3, 4 ]
+    check counts.cumsummed == [ 1, 3, 6, 10 ]
+
+  test "cumsum float seq return":
+    let counts = [ 1.0, 2.0, 3.0, 4.0 ]
+    check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ]
+
+  test "cumsum int in-place":
+    var counts = [ 1, 2, 3, 4 ]
+    counts.cumsum
+    check counts == [ 1, 3, 6, 10 ]
+
+  test "cumsum float in-place":
+    var counts = [ 1.0, 2.0, 3.0, 4.0 ]
+    counts.cumsum
+    check counts == [ 1.0, 3.0, 6.0, 10.0 ]
+
+suite "random sample":
+  test "non-uniform array sample unnormalized int CDF":
+    let values = [ 10, 20, 30, 40, 50 ] # values
+    let counts = [ 4, 3, 2, 1, 0 ]      # weights aka unnormalized probabilities
+    var histo = initCountTable[int]()
+    let cdf = counts.cumsummed          # unnormalized CDF
+    for i in 0 ..< 5000:
+      histo.inc(sample(values, cdf))
+    check histo.len == 4                # number of non-zero in `counts`
+    # Any one bin is a binomial random var for n samples, each with prob p of
+    # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for
+    # big n.  So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while
+    # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01.
+    for i, c in counts:
+      if c == 0:
+        check values[i] notin histo
+        continue
+      let p = float(c) / float(cdf[^1])
+      let n = 5000.0
+      let expected = p * n
+      let stdDev = sqrt(n * p * (1.0 - p))
+      check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
+
+  test "non-uniform array sample normalized float CDF":
+    let values = [ 10, 20, 30, 40, 50 ]     # values
+    let counts = [ 0.4, 0.3, 0.2, 0.1, 0 ]  # probabilities
+    var histo = initCountTable[int]()
+    let cdf = counts.cumsummed              # normalized CDF
+    for i in 0 ..< 5000:
+      histo.inc(sample(values, cdf))
+    check histo.len == 4                    # number of non-zero in ``counts``
+    for i, c in counts:
+      if c == 0:
+        check values[i] notin histo
+        continue
+      let p = float(c) / float(cdf[^1])
+      let n = 5000.0
+      let expected = p * n
+      let stdDev = sqrt(n * p * (1.0 - p))
+      # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01.
+      check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
+
 suite "^":
   test "compiles for valid types":
     check: compiles(5 ^ 2)
diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim
index 5792b6282..5e2e327f4 100644
--- a/tests/stdlib/tmget.nim
+++ b/tests/stdlib/tmget.nim
@@ -11,10 +11,10 @@ Can't access 6
 Can't access 6
 10
 11
-Can't access 6
+0
 10
 11
-Can't access 6
+0
 10
 11
 Can't access 6
@@ -85,7 +85,7 @@ block:
   except KeyError:
     echo "Can't access 6"
   echo x[5]
-  x[5] += 1
+  x.inc 5, 1
   var c = x[5]
   echo c
 
@@ -97,7 +97,7 @@ block:
   except KeyError:
     echo "Can't access 6"
   echo x[5]
-  x[5] += 1
+  x.inc 5, 1
   var c = x[5]
   echo c
 
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index 467f64fff..e4e14d5a1 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -189,62 +189,44 @@ block walkDirRec:
 
   removeDir("walkdir_test")
 
-block normalizedPath:
-  when defined(posix):
-    block relative:
-      doAssert normalizedPath(".") == "."
-      doAssert normalizedPath("..") == ".."
-      doAssert normalizedPath("../") == ".."
-      doAssert normalizedPath("../..") == "../.."
-      doAssert normalizedPath("../a/..") == ".."
-      doAssert normalizedPath("../a/../") == ".."
-      doAssert normalizedPath("./") == "."
-
-    block absolute:
-      doAssert normalizedPath("/") == "/"
-      doAssert normalizedPath("/.") == "/"
-      doAssert normalizedPath("/..") == "/"
-      doAssert normalizedPath("/../") == "/"
-      doAssert normalizedPath("/../..") == "/"
-      doAssert normalizedPath("/../../") == "/"
-      doAssert normalizedPath("/../../../") == "/"
-      doAssert normalizedPath("/a/b/../../foo") == "/foo"
-      doAssert normalizedPath("/a/b/../../../foo") == "/foo"
-      doAssert normalizedPath("/./") == "/"
-      doAssert normalizedPath("//") == "/"
-      doAssert normalizedPath("///") == "/"
-      doAssert normalizedPath("/a//b") == "/a/b"
-      doAssert normalizedPath("/a///b") == "/a/b"
-      doAssert normalizedPath("/a/b/c/..") == "/a/b"
-      doAssert normalizedPath("/a/b/c/../") == "/a/b"
+when not defined(windows):
+  block walkDirRelative:
+    createDir("walkdir_test")
+    createSymlink(".", "walkdir_test/c")
+    for k, p in walkDir("walkdir_test", true):
+      doAssert k == pcLinkToDir
+    removeDir("walkdir_test")
 
-  else:
-    block relative:
-      doAssert normalizedPath(".") == "."
-      doAssert normalizedPath("..") == ".."
-      doAssert normalizedPath("..\\") == ".."
-      doAssert normalizedPath("..\\..") == "..\\.."
-      doAssert normalizedPath("..\\a\\..") == ".."
-      doAssert normalizedPath("..\\a\\..\\") == ".."
-      doAssert normalizedPath(".\\") == "."
-
-    block absolute:
-      doAssert normalizedPath("\\") == "\\"
-      doAssert normalizedPath("\\.") == "\\"
-      doAssert normalizedPath("\\..") == "\\"
-      doAssert normalizedPath("\\..\\") == "\\"
-      doAssert normalizedPath("\\..\\..") == "\\"
-      doAssert normalizedPath("\\..\\..\\") == "\\"
-      doAssert normalizedPath("\\..\\..\\..\\") == "\\"
-      doAssert normalizedPath("\\a\\b\\..\\..\\foo") == "\\foo"
-      doAssert normalizedPath("\\a\\b\\..\\..\\..\\foo") == "\\foo"
-      doAssert normalizedPath("\\.\\") == "\\"
-      doAssert normalizedPath("\\\\") == "\\"
-      doAssert normalizedPath("\\\\\\") == "\\"
-      doAssert normalizedPath("\\a\\\\b") == "\\a\\b"
-      doAssert normalizedPath("\\a\\\\\\b") == "\\a\\b"
-      doAssert normalizedPath("\\a\\b\\c\\..") == "\\a\\b"
-      doAssert normalizedPath("\\a\\b\\c\\..\\") == "\\a\\b"
+block normalizedPath:
+  doAssert normalizedPath("") == ""
+  block relative:
+    doAssert normalizedPath(".") == "."
+    doAssert normalizedPath("foo/..") == "."
+    doAssert normalizedPath("foo//../bar/.") == "bar"
+    doAssert normalizedPath("..") == ".."
+    doAssert normalizedPath("../") == ".."
+    doAssert normalizedPath("../..") == unixToNativePath"../.."
+    doAssert normalizedPath("../a/..") == ".."
+    doAssert normalizedPath("../a/../") == ".."
+    doAssert normalizedPath("./") == "."
+
+  block absolute:
+    doAssert normalizedPath("/") == unixToNativePath"/"
+    doAssert normalizedPath("/.") == unixToNativePath"/"
+    doAssert normalizedPath("/..") == unixToNativePath"/.."
+    doAssert normalizedPath("/../") == unixToNativePath"/.."
+    doAssert normalizedPath("/../..") == unixToNativePath"/../.."
+    doAssert normalizedPath("/../../") == unixToNativePath"/../.."
+    doAssert normalizedPath("/../../../") == unixToNativePath"/../../.."
+    doAssert normalizedPath("/a/b/../../foo") == unixToNativePath"/foo"
+    doAssert normalizedPath("/a/b/../../../foo") == unixToNativePath"/../foo"
+    doAssert normalizedPath("/./") == unixToNativePath"/"
+    doAssert normalizedPath("//") == unixToNativePath"/"
+    doAssert normalizedPath("///") == unixToNativePath"/"
+    doAssert normalizedPath("/a//b") == unixToNativePath"/a/b"
+    doAssert normalizedPath("/a///b") == unixToNativePath"/a/b"
+    doAssert normalizedPath("/a/b/c/..") == unixToNativePath"/a/b"
+    doAssert normalizedPath("/a/b/c/../") == unixToNativePath"/a/b"
 
 block isHidden:
   when defined(posix):
@@ -265,3 +247,21 @@ block absolutePath:
     doAssert absolutePath("a", "/b/c") == "/b/c" / "a"
     doAssert absolutePath("/a", "b/") == "/a"
 
+block splitFile:
+  doAssert splitFile("") == ("", "", "")
+  doAssert splitFile("abc/") == ("abc", "", "")
+  doAssert splitFile("/") == ("/", "", "")
+  doAssert splitFile("./abc") == (".", "abc", "")
+  doAssert splitFile(".txt") == ("", ".txt", "")
+  doAssert splitFile("abc/.txt") == ("abc", ".txt", "")
+  doAssert splitFile("abc") == ("", "abc", "")
+  doAssert splitFile("abc.txt") == ("", "abc", ".txt")
+  doAssert splitFile("/abc.txt") == ("/", "abc", ".txt")
+  doAssert splitFile("/foo/abc.txt") == ("/foo", "abc", ".txt")
+  doAssert splitFile("/foo/abc.txt.gz") == ("/foo", "abc.txt", ".gz")
+  doAssert splitFile(".") == ("", ".", "")
+  doAssert splitFile("abc/.") == ("abc", ".", "")
+  doAssert splitFile("..") == ("", "..", "")
+  doAssert splitFile("a/..") == ("a", "..", "")
+
+# execShellCmd is tested in tosproc
diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim
index 9d57d4574..b8d3be9bb 100644
--- a/tests/stdlib/tosproc.nim
+++ b/tests/stdlib/tosproc.nim
@@ -1,23 +1,99 @@
-discard """
-  output: ""
-"""
 # test the osproc module
 
-import os, osproc
+import stdtest/specialpaths
+import "../.." / compiler/unittest_light
 
-block execProcessTest:
-  let dir = parentDir(currentSourcePath())
-  let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim"))
-  doAssert err == 0
-  let exePath = dir / addFileExt("osproctest", ExeExt)
-  let outStr1 = execProcess(exePath, workingDir=dir, args=["foo", "b A r"], options={})
-  doAssert outStr1 == dir & "\nfoo\nb A r\n"
+when defined(case_testfile): # compiled test file for child process
+  from posix import exitnow
+  proc c_exit2(code: c_int): void {.importc: "_exit", header: "<unistd.h>".}
+  import os
+  var a = 0
+  proc fun(b = 0) =
+    a.inc
+    if a mod 10000000 == 0: # prevents optimizing it away
+      echo a
+    fun(b+1)
 
-  const testDir = "t e st"
-  createDir(testDir)
-  doAssert dirExists(testDir)
-  let outStr2 = execProcess(exePath, workingDir=testDir, args=["x yz"], options={})
-  doAssert outStr2 == absolutePath(testDir) & "\nx yz\n"
+  proc main() =
+    let args = commandLineParams()
+    echo (msg: "child binary", pid: getCurrentProcessId())
+    let arg = args[0]
+    echo (arg: arg)
+    case arg
+    of "exit_0":
+      if true: quit(0)
+    of "exitnow_139":
+      if true: exitnow(139)
+    of "c_exit2_139":
+      if true: c_exit2(139)
+    of "quit_139":
+      # `exitStatusLikeShell` doesn't distinguish between a process that
+      # exit(139) and a process that gets killed with `SIGSEGV` because
+      # 139 = 11 + 128 = SIGSEGV + 128.
+      # However, as #10249 shows, this leads to bad debugging experience
+      # when a child process dies with SIGSEGV, leaving no trace of why it
+      # failed. The shell (and lldb debugger) solves that by inserting a
+      # helpful msg: `segmentation fault` when it detects a signal killed
+      # the child.
+      # todo: expose an API that will show more diagnostic, returing
+      # (exitCode, signal) instead of just `shellExitCode`.
+      if true: quit(139)
+    of "exit_recursion": # stack overflow by infinite recursion
+      fun()
+      echo a
+    of "exit_array": # bad array access
+      echo args[1]
+  main()
 
-  removeDir(testDir)
-  removeFile(exePath)
+else:
+
+  import os, osproc, strutils, posix
+
+  block execShellCmdTest:
+    ## first, compile child program
+    const nim = getCurrentCompilerExe()
+    const sourcePath = currentSourcePath()
+    let output = buildDir / "D20190111T024543".addFileExt(ExeExt)
+    let cmd = "$# c -o:$# -d:release -d:case_testfile $#" % [nim, output,
+        sourcePath]
+    # we're testing `execShellCmd` so don't rely on it to compile test file
+    # note: this should be exported in posix.nim
+    proc c_system(cmd: cstring): cint {.importc: "system",
+      header: "<stdlib.h>".}
+    assertEquals c_system(cmd), 0
+
+    ## use it
+    template runTest(arg: string, expected: int) =
+      echo (arg2: arg, expected2: expected)
+      assertEquals execShellCmd(output & " " & arg), expected
+
+    runTest("exit_0", 0)
+    runTest("exitnow_139", 139)
+    runTest("c_exit2_139", 139)
+    runTest("quit_139", 139)
+    runTest("exit_array", 1)
+    when defined(posix): # on windows, -1073741571
+      runTest("exit_recursion", SIGSEGV.int + 128) # bug #10273: was returning 0
+      assertEquals exitStatusLikeShell(SIGSEGV), SIGSEGV + 128.cint
+
+  block execProcessTest:
+    let dir = parentDir(currentSourcePath())
+    let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim"))
+    doAssert err == 0
+    let exePath = dir / addFileExt("osproctest", ExeExt)
+    let outStr1 = execProcess(exePath, workingDir = dir, args = ["foo",
+        "b A r"], options = {})
+    doAssert outStr1 == dir & "\nfoo\nb A r\n"
+
+    const testDir = "t e st"
+    createDir(testDir)
+    doAssert dirExists(testDir)
+    let outStr2 = execProcess(exePath, workingDir = testDir, args = ["x yz"],
+        options = {})
+    doAssert outStr2 == absolutePath(testDir) & "\nx yz\n"
+
+    removeDir(testDir)
+    try:
+      removeFile(exePath)
+    except OSError:
+      discard
diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim
index 33cb581ef..c1941bd38 100644
--- a/tests/stdlib/trepr.nim
+++ b/tests/stdlib/trepr.nim
@@ -1,5 +1,6 @@
 discard """
-  output: "{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}"
+  output: '''{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
+[1, 2, 3, 4, 5, 6]'''
 """
 
 type
@@ -25,3 +26,11 @@ when false:
 #  "a", "b", "c", "d", "e"
 #]
 #echo(repr(testseq))
+
+# bug #7878
+proc test(variable: var openarray[int]) =
+  echo repr(variable)
+
+var arr = [1, 2, 3, 4, 5, 6]
+
+test(arr)
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index ed87b15ac..3999c968f 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -245,6 +245,17 @@ suite "ttimes":
     parseTestExcp("12345", "uuuu")
     parseTestExcp("-1 BC", "UUUU g")
 
+  test "incorrect inputs: invalid sign":
+    parseTestExcp("+1", "YYYY")
+    parseTestExcp("+1", "dd")
+    parseTestExcp("+1", "MM")
+    parseTestExcp("+1", "hh")
+    parseTestExcp("+1", "mm")
+    parseTestExcp("+1", "ss")
+
+  test "_ as a separator":
+    discard parse("2000_01_01", "YYYY'_'MM'_'dd")
+
   test "dynamic timezone":
     let tz = staticTz(seconds = -9000)
     let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz)
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index c8656bbff..65baefef0 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -50,7 +50,6 @@ test "unittest multiple requires":
 import math, random
 from strutils import parseInt
 proc defectiveRobot() =
-  randomize()
   case random(1..4)
   of 1: raise newException(OSError, "CANNOT COMPUTE!")
   of 2: discard parseInt("Hello World!")
diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim
index 3bbb5eff1..a20e6b3bf 100644
--- a/tests/system/tsystem_misc.nim
+++ b/tests/system/tsystem_misc.nim
@@ -30,6 +30,20 @@ discard """
 '''
 """
 
+
+block:
+  const a2 = $(int)
+  const a3 = $int
+  doAssert a2 == "int"
+  doAssert a3 == "int"
+
+  proc fun[T: typedesc](t: T) =
+    const a2 = $(t)
+    const a3 = $t
+    doAssert a2 == "int"
+    doAssert a3 == "int"
+  fun(int)
+
 # check high/low implementations
 doAssert high(int) > low(int)
 doAssert high(int8) > low(int8)
@@ -98,6 +112,18 @@ doAssertRaises(IndexError):
   foo(toOpenArray(arrNeg, -1, 0))
 doAssertRaises(IndexError):
   foo(toOpenArray(arrNeg, -1, -3))
+doAssertRaises(Exception):
+  raise newException(Exception, "foo")
+
+block:
+  var didThrow = false
+  try:
+    doAssertRaises(IndexError): # should fail since it's wrong exception
+      raise newException(FieldError, "foo")
+  except AssertionError:
+    # ok, throwing was correct behavior
+    didThrow = true
+  doAssert didThrow
 
 type seqqType = ptr UncheckedArray[int]
 let qData = cast[seqqType](addr seqq[0])
@@ -122,3 +148,14 @@ let a = @[1, 2, 3]
 
 # a.boundedOpenArray(1, 2).foo()  # Works
 echo a.boundedOpenArray(1, 2).len # Internal compiler error
+
+block: # `$`*[T: tuple|object](x: T)
+  doAssert $(foo1:0, bar1:"a") == """(foo1: 0, bar1: "a")"""
+  doAssert $(foo1:0, ) == """(foo1: 0)"""
+  doAssert $(0, "a") == """(0, "a")"""
+  doAssert $(0, ) == "(0,)"
+  type Foo = object
+    x:int
+    x2:float
+  doAssert $Foo(x:2) == "(x: 2, x2: 0.0)"
+  doAssert $() == "()"
diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims
index b9a6097c2..3efbb0a4c 100644
--- a/tests/test_nimscript.nims
+++ b/tests/test_nimscript.nims
@@ -17,7 +17,6 @@ import parseutils
 import deques
 import sequtils
 import strutils
-import subexes
 import tables
 import unicode
 import uri
diff --git a/tests/testament/tshouldfail.nim b/tests/testament/tshouldfail.nim
index d35dd99ac..ebf941fab 100644
--- a/tests/testament/tshouldfail.nim
+++ b/tests/testament/tshouldfail.nim
@@ -2,30 +2,30 @@ discard """
 cmd: "testament/tester --directory:testament --colors:off --backendLogging:off --nim:../compiler/nim category shouldfail"
 action: compile
 nimout: '''
-FAIL: tccodecheck.nim C
+FAIL: tests/shouldfail/tccodecheck.nim C
 Failure: reCodegenFailure
 Expected:
 baz
-FAIL: tcolumn.nim C
+FAIL: tests/shouldfail/tcolumn.nim C
 Failure: reLinesDiffer
-FAIL: terrormsg.nim C
+FAIL: tests/shouldfail/terrormsg.nim C
 Failure: reMsgsDiffer
-FAIL: texitcode1.nim C
+FAIL: tests/shouldfail/texitcode1.nim C
 Failure: reExitcodesDiffer
-FAIL: tfile.nim C
+FAIL: tests/shouldfail/tfile.nim C
 Failure: reFilesDiffer
-FAIL: tline.nim C
+FAIL: tests/shouldfail/tline.nim C
 Failure: reLinesDiffer
-FAIL: tmaxcodesize.nim C
+FAIL: tests/shouldfail/tmaxcodesize.nim C
 Failure: reCodegenFailure
 max allowed size: 1
-FAIL: tnimout.nim C
+FAIL: tests/shouldfail/tnimout.nim C
 Failure: reMsgsDiffer
-FAIL: toutput.nim C
+FAIL: tests/shouldfail/toutput.nim C
 Failure: reOutputsDiffer
-FAIL: toutputsub.nim C
+FAIL: tests/shouldfail/toutputsub.nim C
 Failure: reOutputsDiffer
-FAIL: tsortoutput.nim C
+FAIL: tests/shouldfail/tsortoutput.nim C
 Failure: reOutputsDiffer
 '''
 """
diff --git a/tests/threads/tactors.nim b/tests/threads/tactors.nim
deleted file mode 100644
index ea052b9bd..000000000
--- a/tests/threads/tactors.nim
+++ /dev/null
@@ -1,13 +0,0 @@
-discard """
-  outputsub: "150"
-"""
-
-import actors
-
-var
-  pool: ActorPool[int, void]
-createActorPool(pool)
-for i in 0 ..< 300:
-  pool.spawn(i, proc (x: int) {.thread.} = echo x)
-pool.join()
-
diff --git a/tests/threads/tactors2.nim b/tests/threads/tactors2.nim
deleted file mode 100644
index e8afe203c..000000000
--- a/tests/threads/tactors2.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  output: "1"
-"""
-
-import actors
-
-type
-  some_type {.pure, final.} = object
-    bla: int
-
-proc thread_proc(input: some_type): some_type {.thread.} =
-  result.bla = 1
-
-proc main() =
-  var actorPool: ActorPool[some_type, some_type]
-  createActorPool(actorPool, 1)
-
-  var some_data: some_type
-
-  var inchannel = spawn(actorPool, some_data, thread_proc)
-  var recv_data = ^inchannel
-  close(inchannel[])
-  echo recv_data.bla
-
-main()
diff --git a/tests/threads/trecursive_actor.nim b/tests/threads/trecursive_actor.nim
deleted file mode 100644
index d7072aa53..000000000
--- a/tests/threads/trecursive_actor.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  disabled: yes
-  outputsub: "0"
-"""
-
-import actors
-
-var
-  a: TActorPool[int, void]
-createActorPool(a)
-
-proc task(i: int) {.thread.} =
-  echo i
-  if i != 0: a.spawn (i-1, task)
-
-# count from 9 till 0 and check 0 is somewhere in the output
-a.spawn(9, task)
-a.join()
-
-
diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim
index d500c49de..c1367cb1b 100644
--- a/tests/trmacros/trmacros_various2.nim
+++ b/tests/trmacros/trmacros_various2.nim
@@ -77,3 +77,13 @@ block tstar:
 
   # check that it's been optimized properly:
   doAssert calls == 1
+
+# bug #7524
+template in_to_out(typIn, typOut: typedesc) =
+  proc to_out(x: typIn{lit}): typOut = result = ord(x)
+
+# Generating the proc via template doesn't work
+in_to_out(char, int)
+
+# This works
+proc to_out2(x: char{lit}): int = result = ord(x)
diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim
index 010893ced..94a2f3ff0 100644
--- a/tests/tuples/ttuples_various.nim
+++ b/tests/tuples/ttuples_various.nim
@@ -66,6 +66,22 @@ block unpack_asgn:
 
 
 
+block unpack_const:
+  const (a, ) = (1, )
+  doAssert a == 1
+
+  const (b, c) = (2, 3)
+  doAssert b == 2
+  doAssert c == 3
+
+  # bug #10098
+  const (x, y, z) = (4, 5, 6)
+  doAssert x == 4
+  doAssert y == 5
+  doAssert z == 6
+
+
+
 block tuple_subscript:
   proc`[]` (t: tuple, key: string): string =
     for name, field in fieldPairs(t):
diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim
index e09c53407..1b210b2bf 100644
--- a/tests/tuples/tuple_with_nil.nim
+++ b/tests/tuples/tuple_with_nil.nim
@@ -4,7 +4,6 @@ import parseutils
 import unicode
 import math
 import fenv
-#import unsigned
 import pegs
 import streams
 
diff --git a/tests/typerel/t4799.nim b/tests/typerel/t4799.nim
index 075893476..814ad361d 100644
--- a/tests/typerel/t4799.nim
+++ b/tests/typerel/t4799.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   output: "OK"
 """
 
diff --git a/tests/typerel/t4799_1.nim b/tests/typerel/t4799_1.nim
index 549b6bf3c..e66aa1a9a 100644
--- a/tests/typerel/t4799_1.nim
+++ b/tests/typerel/t4799_1.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   outputsub: '''ObjectAssignmentError'''
   exitcode: "1"
 """
diff --git a/tests/typerel/t4799_2.nim b/tests/typerel/t4799_2.nim
index cfd399a6e..ff20c2426 100644
--- a/tests/typerel/t4799_2.nim
+++ b/tests/typerel/t4799_2.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   outputsub: '''ObjectAssignmentError'''
   exitcode: "1"
 """
diff --git a/tests/typerel/t4799_3.nim b/tests/typerel/t4799_3.nim
index 784eee8fc..4a8a158dd 100644
--- a/tests/typerel/t4799_3.nim
+++ b/tests/typerel/t4799_3.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c cpp"
   outputsub: '''ObjectAssignmentError'''
   exitcode: "1"
 """
diff --git a/tests/vm/tcompiletimesideeffects.nim b/tests/vm/tcompiletimesideeffects.nim
new file mode 100644
index 000000000..4cd57b3bd
--- /dev/null
+++ b/tests/vm/tcompiletimesideeffects.nim
@@ -0,0 +1,42 @@
+discard """
+  output:
+'''
+@[0, 1, 2]
+@[3, 4, 5]
+@[0, 1, 2]
+3
+4
+'''
+"""
+
+template runNTimes(n: int, f : untyped) : untyped =
+  var accum: seq[type(f)]
+  for i in 0..n-1:
+    accum.add(f)
+  accum
+
+var state {.compileTime.} : int = 0
+proc fill(): int {.compileTime.} =
+  result = state
+  inc state
+
+# invoke fill() at compile time as a compile time expression
+const C1 = runNTimes(3, fill())
+echo C1
+
+# invoke fill() at compile time as a set of compile time statements
+const C2 =
+  block:
+    runNTimes(3, fill())
+echo C2
+
+# invoke fill() at compile time after a compile time reset of state
+const C3 =
+  block:
+    state = 0
+    runNTimes(3, fill())
+echo C3
+
+# evaluate fill() at compile time and use the results at runtime
+echo fill()
+echo fill()
diff --git a/tests/vm/tgorge.nim b/tests/vm/tgorge.nim
index 11c49a4cc..1f77d2c95 100644
--- a/tests/vm/tgorge.nim
+++ b/tests/vm/tgorge.nim
@@ -10,6 +10,8 @@ import os
 template getScriptDir(): string =
   parentDir(instantiationInfo(-1, true).filename)
 
+# See also simpler test in Nim/tests/vm/tvmops.nim for a simpler
+# cross platform way.
 block gorge:
   const
     execName = when defined(windows): "tgorge.bat" else: "./tgorge.sh"
diff --git a/tests/vm/tissues.nim b/tests/vm/tissues.nim
index 021b902ad..063559d2e 100644
--- a/tests/vm/tissues.nim
+++ b/tests/vm/tissues.nim
@@ -1,16 +1,14 @@
-discard """
-  nimout: "(Field0: 2, Field1: 2, Field2: 2, Field3: 2)"
-"""
-
 import macros
 
-block t9043:
-  proc foo[N: static[int]](dims: array[N, int])=
+block t9043: # issue #9043
+  proc foo[N: static[int]](dims: array[N, int]): string =
     const N1 = N
     const N2 = dims.len
-    static: echo (N, dims.len, N1, N2)
+    const ret = $(N, dims.len, N1, N2)
+    static: doAssert ret == $(N, dims.len, N1, N2)
+    ret
 
-  foo([1, 2])
+  doAssert foo([1, 2]) == "(2, 2, 2, 2)"
 
 block t4952:
   proc doCheck(tree: NimNode) =
diff --git a/tests/vm/treset.nim b/tests/vm/treset.nim
new file mode 100644
index 000000000..56fe19b19
--- /dev/null
+++ b/tests/vm/treset.nim
@@ -0,0 +1,28 @@
+static:
+  type Obj = object
+    field: int
+  var o = Obj(field: 1)
+  reset(o)
+  doAssert o.field == 0
+
+static:
+  var i = 2
+  reset(i)
+  doAssert i == 0
+
+static:
+  var i = new int
+  reset(i)
+  doAssert i.isNil
+
+static:
+  var s = @[1, 2, 3]
+  reset(s)
+  doAssert s == @[]
+
+static:
+  proc f() =
+    var i = 2
+    reset(i)
+    doAssert i == 0
+  f()
\ No newline at end of file
diff --git a/tests/vm/tvmops.nim b/tests/vm/tvmops.nim
new file mode 100644
index 000000000..c9caaf32b
--- /dev/null
+++ b/tests/vm/tvmops.nim
@@ -0,0 +1,47 @@
+#[
+test for vmops.nim
+]#
+import os
+import math
+import strutils
+
+template forceConst(a: untyped): untyped =
+  ## Force evaluation at CT, useful for example here:
+  ## `callFoo(forceConst(getBar1()), getBar2())`
+  ## instead of:
+  ##  block:
+  ##    const a = getBar1()
+  ##    `callFoo(a, getBar2())`
+  const ret = a
+  ret
+
+static:
+  # TODO: add more tests
+  block: #getAppFilename, gorgeEx, gorge
+    const nim = getCurrentCompilerExe()
+    let ret = gorgeEx(nim & " --version")
+    doAssert ret.exitCode == 0
+    doAssert ret.output.contains "Nim Compiler"
+    let ret2 = gorgeEx(nim & " --unexistant")
+    doAssert ret2.exitCode != 0
+    let output3 = gorge(nim & " --version")
+    doAssert output3.contains "Nim Compiler"
+
+  block:
+    const key = "D20181210T175037"
+    const val = "foo"
+    putEnv(key, val)
+    doAssert existsEnv(key)
+    doAssert getEnv(key) == val
+
+  block:
+    # sanity check (we probably don't need to test for all ops)
+    const a1 = arcsin 0.3
+    let a2 = arcsin 0.3
+    doAssert a1 == a2
+
+block:
+  # Check against bugs like #9176
+  doAssert getCurrentCompilerExe() == forceConst(getCurrentCompilerExe())
+  if false: #pending #9176
+    doAssert gorgeEx("unexistant") == forceConst(gorgeEx("unexistant"))
diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim
index 658b9ead7..376f33ae5 100644
--- a/tools/kochdocs.nim
+++ b/tools/kochdocs.nim
@@ -56,12 +56,8 @@ proc nimexec*(cmd: string) =
   exec findNim() & " " & cmd
 
 proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") =
-  # TODO: simplify pending https://github.com/nim-lang/Nim/issues/9513
-  var cmd = findNim() & " " & mode
   let output = outputDir / input.splitFile.name.exe
-  cmd.add " -o:" & output
-  cmd.add " " & options
-  cmd.add " " & input
+  let cmd = findNim() & " " & mode & " -o:" & output & " " & options & " " & input
   exec cmd
 
 const
@@ -126,7 +122,6 @@ lib/js/asyncjs.nim
 lib/pure/os.nim
 lib/pure/strutils.nim
 lib/pure/math.nim
-lib/pure/matchers.nim
 lib/std/editdistance.nim
 lib/std/wordwrap.nim
 lib/experimental/diff.nim
@@ -161,10 +156,8 @@ lib/impure/db_mysql.nim
 lib/impure/db_sqlite.nim
 lib/impure/db_odbc.nim
 lib/pure/db_common.nim
-lib/pure/httpserver.nim
 lib/pure/httpclient.nim
 lib/pure/smtp.nim
-lib/impure/ssl.nim
 lib/pure/ropes.nim
 lib/pure/unidecode/unidecode.nim
 lib/pure/xmlparser.nim
@@ -174,7 +167,6 @@ lib/pure/colors.nim
 lib/pure/mimetypes.nim
 lib/pure/json.nim
 lib/pure/base64.nim
-lib/pure/scgi.nim
 lib/impure/nre.nim
 lib/impure/nre/private/util.nim
 lib/deprecated/pure/sockets.nim
@@ -185,14 +177,12 @@ lib/pure/collections/lists.nim
 lib/pure/collections/sharedlist.nim
 lib/pure/collections/sharedtables.nim
 lib/pure/collections/intsets.nim
-lib/pure/collections/queues.nim
 lib/pure/collections/deques.nim
 lib/pure/encodings.nim
 lib/pure/collections/sequtils.nim
 lib/pure/collections/rtarrays.nim
 lib/pure/cookies.nim
 lib/pure/memfiles.nim
-lib/pure/subexes.nim
 lib/pure/collections/critbits.nim
 lib/core/locks.nim
 lib/core/rlocks.nim
@@ -218,7 +208,6 @@ lib/pure/selectors.nim
 lib/pure/sugar.nim
 lib/pure/collections/chains.nim
 lib/pure/asyncfile.nim
-lib/deprecated/pure/ftpclient.nim
 lib/pure/asyncftpclient.nim
 lib/pure/lenientops.nim
 lib/pure/md5.nim
@@ -229,6 +218,7 @@ lib/pure/collections/heapqueue.nim
 lib/pure/fenv.nim
 lib/std/sha1.nim
 lib/std/varints.nim
+lib/std/time_t.nim
 lib/impure/rdstdin.nim
 lib/wrappers/linenoise/linenoise.nim
 lib/pure/strformat.nim
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index 8c6353e31..0a835b038 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -160,7 +160,7 @@ proc processFile(pattern; filename: string; counter: var int) =
   var reallyReplace = true
   while i < buffer.len:
     let t = findBounds(buffer, pattern, matches, i)
-    if t.first < 0: break
+    if t.first < 0 or t.last < t.first: break
     inc(line, countLines(buffer, i, t.first-1))
 
     var wholeMatch = buffer.substr(t.first, t.last)
@@ -336,7 +336,7 @@ else:
     for f in items(filenames):
       walker(pegp, f, counter)
   else:
-    var reflags = {reStudy, reExtended}
+    var reflags = {reStudy}
     if optIgnoreStyle in options:
       pattern = styleInsensitive(pattern)
     if optWord in options:
diff --git a/tools/vccexe/vccenv.nim b/tools/vccexe/vccenv.nim
index 6ddf2e29a..bd3b0b30a 100644
--- a/tools/vccexe/vccenv.nim
+++ b/tools/vccexe/vccenv.nim
@@ -16,7 +16,7 @@ type
     vs140 = (140, "VS140COMNTOOLS")  ## Visual Studio 2015
 
 const
-  vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall") ## Relative path from the COMNTOOLS path to the vcvarsall file.
+  vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall.bat") ## Relative path from the COMNTOOLS path to the vcvarsall file.
 
 proc vccEnvVcVarsAllPath*(version: VccEnvVersion = vsUndefined): string = 
   ## Returns the path to the VCC Developer Command Prompt executable for the specified VCC version.
@@ -44,4 +44,4 @@ proc vccEnvVcVarsAllPath*(version: VccEnvVersion = vsUndefined): string =
     let key = $version
     let val = getEnv key
     if val.len > 0:
-      result = expandFilename(val & vcvarsallRelativePath)
+      result = try: expandFilename(joinPath(val, vcvarsallRelativePath)) except OSError: ""